mysql死锁无法完全避免,关键在优化事务设计:缩短锁持有时间、统一加锁顺序、确保sql走索引、拆分批量操作、按主键升序查询、禁用动态sql,并合理配置超时与重试机制。

死锁不是“避免”而是“减少概率”,关键在事务设计
MySQL 死锁无法完全避免,因为它是 InnoDB 在并发更新同一组行时的正常保护机制。真正要做的,是让事务持有锁的时间更短、加锁顺序更一致、冲突路径更可控。
常见错误现象:Deadlock found when trying to get lock; try restarting transaction 不代表代码有 bug,但频繁出现说明事务结构不合理。
- 所有
UPDATE/DELETE语句必须走索引,否则会升级为表级锁或间隙锁,大幅增加死锁面 - 事务中先
SELECT ... FOR UPDATE再修改,不如直接UPDATE—— 多一次加锁就多一次竞争机会 - 批量操作拆成小事务,比如 1000 行分 10 次提交,比单个事务锁 1000 行安全得多
加锁顺序不一致是死锁主因,必须统一
两个事务按不同顺序更新 user 和 order 表,就极易触发死锁。InnoDB 不保证语句执行顺序,但可以强制业务层约定顺序。
使用场景:微服务间调用、订单创建含用户扣款、库存预占+日志写入等多表更新逻辑。
- 约定“先操作主表(如
user),再操作从表(如order)”,并在所有服务中严格遵守 - 用
SELECT ... FOR UPDATE时,务必按主键升序查,例如SELECT * FROM order WHERE id IN (10,5,8) ORDER BY id,避免因查询优化器打乱顺序导致加锁错位 - 避免在事务中动态拼接 SQL 或依赖子查询结果决定下一步更新哪张表——这会让加锁路径不可预测
innodb_lock_wait_timeout 和 innodb_deadlock_detect 不是救命稻草
调大 innodb_lock_wait_timeout 只会让死锁等待更久,反而拖垮连接池;关掉 innodb_deadlock_detect 在高并发下可能引发锁等待雪崩。
性能与兼容性影响:
-
innodb_deadlock_detect = ON(默认)是必要开销,5.7+ 已优化为局部检测,不必担心 CPU 爆涨 -
innodb_lock_wait_timeout建议保持默认 50 秒,应用层应主动设更短的超时(如 3–5 秒),并捕获Deadlock found...后退避重试 - 不要用
SET SESSION innodb_lock_wait_timeout = 5动态改,连接复用下容易污染其他请求
如何快速定位谁在抢同一行?看 SHOW ENGINE INNODB STATUS
它输出的 LATEST DETECTED DEADLOCK 区块,比任何监控都直接。重点不是看“哪个事务赢了”,而是比对两段 TRANSACTION 下的 mysql tables in use 和 LOCK WAIT 行。
实操建议:
- 每次上线前,在测试环境故意构造双事务交叉更新,跑一遍
SHOW ENGINE INNODB STATUS,确认锁顺序是否符合预期 - 线上出死锁后,立刻抓取该命令输出,重点关注
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:后面的RECORD LOCKS space id和主键值,反推是哪几行被争抢 - 配合
SELECT * FROM information_schema.INNODB_TRX查当前长事务,常是死锁温床——特别是没 COMMIT 的交互式连接
最易被忽略的一点:唯一索引冲突也会触发死锁。比如两个事务同时 INSERT INTO t (uid) VALUES (123),而 uid 是唯一键,InnoDB 会在 duplicate key 检查时加意向锁,照样可能卡住。这种场景下,应用层幂等控制比调参数更治本。










