一、问题描述
最近工作中发现了一个系统问题,涉及到关于 mysql for update 排他锁,没有阻塞其他事务的问题。
主要的业务为多个内配箱子入库回传修改箱子、内配单状态,内配单状态为配货完成状态,没有被修改为上架完成状态。
一个内配单可能存在多个商品,一个内配单发货可能会发不同的箱子,每个箱子可能包含不同的内配单的商品。即内配单与箱子的关系是多对多。
二、原因分析
数据库的隔离级别:读已提交
2.1 箱子入库回传流程
select
c.id as id
from
table_c c
where
c.id in (xxx, xxx)
and not exists (
select
1
from
table_s s,
table_b b
where
s.chuku_id = c.id
and s.box_id = b.id
and b.work_statue != '4'
) for
update
2.2 FOR UPDATE 语句的用法
在 MySQL 中,SELECT ... FOR UPDATE
语句是一种锁定读取操作,它锁定语句返回的所有行,以防止其他事务在这些行上执行 UPDATE
或 DELETE
操作直到当前事务提交。
以下是 FOR UPDATE
锁定的一些关键点: 锁定范围:
FOR UPDATE
会对查询结果中的所有行设置排他锁(X锁)。- 如果查询中包含了 WHERE 子句,那么只有满足条件的行会被锁定。
- 如果查询中包含了 join 操作,那么涉及的所有表的相关行都会被锁定。 锁定行为:
- 其他事务不能对这些行执行
UPDATE
或DELETE
操作,直到当前事务提交。 - 其他事务仍然可以读取这些行,因为
SELECT ... FOR UPDATE
不会阻止共享锁(S锁)。 锁定持续时间: - 锁定会一直持续到事务结束(提交或回滚)。 死锁风险:
- 使用
FOR UPDATE
时,如果两个事务尝试锁定对方已经锁定的行,可能会导致死锁。 使用限制: FOR UPDATE
只能在支持行级锁的存储引擎(如 InnoDB)上使用。- 要使用
FOR UPDATE
,必须开启一个事务(使用BEGIN
或START TRANSACTION
)。
例如:
BEGIN;
SELECT * FROM your_table WHERE condition FOR UPDATE;
UPDATE your_table SET column = value WHERE condition;
COMMIT;
上面的例子中,SELECT ... FOR UPDATE
语句会锁定所有满足 WHERE condition
的行,然后 UPDATE
语句可以安全地对这些行进行修改。
请记住,使用 FOR UPDATE
时要谨慎,因为它可能会影响并发性和性能。只在必要时使用它,并确保事务尽可能短且高效。
2.3 原因总结
如果内配箱子涉及到相同的内配单,且存在箱子包含多个内配单,且先于其他箱子的事务(只包含一个内配单)执行 for update 的SQL语句,那么后边的事务会出现不会被阻塞的问题。
如果多个箱子都包含相同内配单号,且只有一个,那么多个事务同时执行 for update 语句时就会阻塞(正确)。 一句话描述问题,执行 for update SQL语句时,想要达到多个事务阻塞效果,必须两个事务执行select...for update 数据的查询结果一样。
三、解决方案
1、针对箱子入库回传增加分布式锁方案流程
- 箱子入库回传时,先查询箱子对应的内配单号
- 然后将内配单按照升序排序,分别设置redis分布式锁
- 全部加锁成功之后,处理业务
- 如果任何一个内配单加锁失败,说明被其他箱子加锁了,抛出异常,重试
优点:仅仅改动箱子入库回传这块代码 缺点:需要开发代码,上线测试
2、修改 for update 的SQL,增加独立的加锁语句
修改逻辑:
- 将箱子对应的内配单ID 进行 for update的查询,且不加其他条件,确保可以其他可以锁定住其他事务的查询
select
c.id as id
from
table_c c
where
c.id in (xxx, xxx) for
update
- 再查询对应的已完成的箱子信息
select
c.id as id
from
table_c c
where
c.id in (xxx, xxx)
and not exists (
select
1
from
table_s s,
table_b b
where
s.chuku_id = c.id
and s.box_id = b.id
and b.work_statue != '4'
)
优点:不用增加分布式锁 缺点:增加了两次数据库查询,一次是利用 for update 语句负责加锁,另一次是查询满足条件的数据,可能会影响处理效率。
参考: https://dogslee.top/2021/08/25/%E8%AE%BE%E7%BD%AEMySQL%E7%9A%84%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB/ https://www.51cto.com/article/745157.html