多个事务同时修改同一索引块(如主键或唯一索引)时,确实容易引发锁冲突甚至死锁,尤其在高并发场景下。
1、原因
InnoDB 锁机制基于索引:所有行级锁实际加在索引记录上,而非物理行。若多个事务并发操作同一索引块(如主键冲突、唯一键重复等),会竞争同一索引项的锁 。
锁类型冲突:
插入/更新/删除操作通常需获取排他锁(X锁)。
唯一性检查(如 INSERT 或 REPLACE INTO)可能先加 共享锁(S锁) 进行唯一校验,再升级为 X 锁 。
循环等待导致死锁:当事务 A 持有索引块 X 的锁并等待 Y,而事务 B 持有 Y 并等待 X,形成死锁,MySQL 会自动回滚其中一个事务 。
2、典型场景
- 并发插入相同主键/唯一值
多个事务同时插入具有相同主键或唯一索引值的记录,导致唯一性检查阶段竞争 S 锁,提交时需升级为 X 锁,引发死锁 。
- 锁顺序不一致
事务 A 先更新记录 1 再更新记录 2,事务 B 先更新记录 2 再更新记录 1,若并发执行,可能互相等待对方持有的索引锁 。
- 长事务持有锁时间过久
事务未及时提交,持续占用索引块锁,阻塞其他事务,增加死锁概率 。
3、解决方案
统一加锁顺序:确保所有事务按相同顺序(如按主键升序)访问记录,避免循环等待 。
缩短事务范围:减少事务内操作,避免调用外部接口或耗时逻辑,尽快提交或回滚 。
调整隔离级别:在业务允许前提下,使用 读已提交(RC) 隔离级别,可关闭间隙锁,降低死锁风险 。
应用层重试机制:捕获死锁异常(如 MySQL 错误 1213),自动重试被回滚的事务 。
优化索引设计:避免在高频更新列上创建复合索引,减少索引页锁竞争 。
💡 提示:可通过 SHOW ENGINE INNODB STATUS\G 查看最近一次死锁详情,定位冲突事务和资源 。