MySQL 之 SELECT FOR UPDATE

介绍

FOR UPDATE 是一种行级锁,又叫排它锁。仅适用于 InnoDB,并且必须开启事务,在 BEGINCOMMIT 之间才生效。

准备

  • 创建表
CREATE TABLE `goods_order` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `order_number` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '订单编号',
  `user_id` bigint DEFAULT NULL COMMENT '下单用户id',
  `goods_id` bigint DEFAULT NULL COMMENT '商品id',
  `state` int DEFAULT NULL COMMENT '1:待支付 2:已支付 3:已关闭',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `order_number` (`order_number`)
)

开始

开启两个 MySQL 命令窗口

  • 命令窗口1
BEGIN
SELECT id, user_id, goods_id, state FROM goods_order WHERE id = 2 FOR UPDATE
UPDATE goods_order SET state = 2 WHERE id=2
COMMIT
  • 命令窗口2
BEGIN
SELECT id, user_id, goods_id, state FROM goods_order WHERE id = 2 FOR UPDATE
UPDATE goods_order SET state = 3 WHERE id=2
COMMIT

命令窗口1 执行完 SELECT ... FOR UPDATE 后(此时事务还未结束), 命令窗口2 执行 SELECT ... FOR UPDATE 语句时将会阻塞在那,直到 命令窗口1 中的事务结束(执行完 COMMIT)。

其中一个使用场景是用于修改订单状态,修改订单状态往往需要两个步骤:

  1. 查询订单状态。
  2. 修改订单状态。

当有两个任务同时请求时,有可能出现如下情况:

  1. 任务A查询到订单状态为1。
  2. 任务B查询到订单状态为1。
  3. 任务A修改订单状态为2。
  4. 任务B修改订单状态为3。

其中,任务B将订单状态改为3的前提是订单状态为1,但是上述情况下任务B修改订单时订单状态已变成2了,并不符合预期,通过 SELECT ... FRO UPDATE 就可以解决上述问题。

你可能感兴趣的:(MySQL 之 SELECT FOR UPDATE)