mysql死锁如何避免_mysql事务锁优化

5次阅读

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

mysql死锁如何避免_mysql事务锁优化

死锁不是“避免”而是“减少概率”,关键在事务设计

MySQL 死锁无法完全避免,因为它是 InnoDB 在并发更新同一组行时的正常保护机制。真正要做的,是让事务持有锁的时间更短、加锁顺序更一致、冲突路径更可控。

常见错误现象:Deadlock found when trying to get lock; try restarting transaction 不代表代码有 bug,但频繁出现说明事务结构不合理。

  • 所有 UPDATE/delete 语句必须走索引,否则会升级为表级锁或间隙锁,大幅增加死锁面
  • 事务中先 select ... for UPDATE 再修改,不如直接 UPDATE —— 多一次加锁就多一次竞争机会
  • 批量操作拆成小事务,比如 1000 行分 10 次提交,比单个事务锁 1000 行安全得多

加锁顺序不一致是死锁主因,必须统一

两个事务按不同顺序更新 userorder 表,就极易触发死锁。InnoDB 不保证语句执行顺序,但可以强制业务层约定顺序。

使用场景:微服务间调用、订单创建含用户扣款、库存预占+日志写入等多表更新逻辑。

  • 约定“先操作主表(如 user),再操作从表(如 order)”,并在所有服务中严格遵守
  • SELECT ... FOR UPDATE 时,务必按主键升序查,例如 SELECT * FROM order WHERE id IN (10,5,8) ORDER BY id,避免因查询优化器打乱顺序导致加锁错位
  • 避免在事务中动态拼接 SQL 或依赖子查询结果决定下一步更新哪张表——这会让加锁路径不可预测

innodb_lock_wait_timeoutinnodb_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 useLOCK 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 检查时加意向锁,照样可能卡住。这种场景下,应用层幂等控制比调参数更治本。

text=ZqhQzanResources