介绍
FOR UPDATE
是一种行级锁,又叫排它锁。仅适用于 InnoDB
,并且必须开启事务,在 BEGIN
与 COMMIT
之间才生效。
准备
- 创建表
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
)。
其中一个使用场景是用于修改订单状态,修改订单状态往往需要两个步骤:
- 查询订单状态。
- 修改订单状态。
当有两个任务同时请求时,有可能出现如下情况:
- 任务A查询到订单状态为1。
- 任务B查询到订单状态为1。
- 任务A修改订单状态为2。
- 任务B修改订单状态为3。
其中,任务B将订单状态改为3的前提是订单状态为1,但是上述情况下任务B修改订单时订单状态已变成2了,并不符合预期,通过 SELECT ... FRO UPDATE
就可以解决上述问题。
网友评论