mysql如何避免死锁问题_mysql死锁处理方案

1次阅读

死锁是并发设计的必然结果而非配置问题;需按固定顺序访问表和索引、缩短事务生命周期、开启死锁日志并自动重试。

mysql如何避免死锁问题_mysql死锁处理方案

死锁不是配置问题,而是事务执行顺序和资源争抢的必然结果

mysqlInnoDB 引擎在可重复读(REPEATABLE READ)隔离级别下,通过行锁 + 间隙锁(Next-Key Lock)实现并发控制,但这也让死锁无法完全避免——它不是 bug,是并发设计的副产品。真正能做的,是把死锁概率压到业务可接受水平,并确保系统能快速恢复。

按固定顺序访问表和索引,是最有效的预防手段

90% 的死锁源于多个事务以不同顺序更新同一组行。比如事务 A 先更新 user_id=100 再更新 user_id=200,而事务 B 反过来操作,就极易触发死锁。

  • 所有涉及多行更新的逻辑,统一按主键(或某个唯一索引)升序排列后再执行:
    UPDATE accounts SET balance = balance - 100 WHERE user_id IN (100, 200) ORDER BY user_id;
  • 跨表操作时,约定固定的表访问顺序(如总是先 ordersorder_items),并在代码注释中明确写出
  • 避免在应用层用循环逐条 UPDATE,改用单条 INSERT ... ON DUPLICATE KEY UPDATE 或批量 UPDATE ... CASE WHEN

缩短事务生命周期,减少锁持有时间

长事务 = 更长的锁持有时间 = 更高的死锁概率。常见陷阱包括:在事务里调用外部 http 接口、做复杂计算、或等待用户输入。

  • 把非数据库操作(日志记录、消息发送、缓存更新等)全部移到 COMMIT 之后
  • 避免在事务中使用 select ... for UPDATE 锁住大量无关行;必要时加 WHERE 条件并确认走了索引
  • 检查慢查询日志,定位执行时间 > 100ms 的事务,优先优化其 SQL 或拆分逻辑
  • 设置 innodb_lock_wait_timeout 为较低值(如 10–30 秒),让死锁失败更快暴露,而不是卡住整个连接

如何从错误中快速定位和验证死锁原因

MySQL 不会静默吞掉死锁,而是抛出明确错误:Deadlock found when trying to get lock; try restarting transaction。关键是要立刻拿到上下文。

  • 开启死锁日志:SET GLOBAL innodb_print_all_deadlocks = ON,日志会写入 MySQL 错误文件,包含每个事务持有的锁、等待的锁、SQL 语句片段
  • 不要只看报错事务的 SQL,重点比对日志中两个(或多个)事务的「waiting for …」和「holds the lock(s)」部分,找出交叉等待链
  • SHOW ENGINE INNODB STATUSG 查看最近一次死锁详情(注意:只保留最后一次,需及时捕获)
  • 在应用层捕获 ER_LOCK_DEADLOCK(错误码 1213),自动重试 1–2 次,但必须限制重试次数,防止雪崩

死锁排查最耗时间的地方,往往不是看不懂日志,而是事务边界不清晰、SQL 实际执行计划和预期不一致、或者测试环境没复现压力场景。上线前用 pt-deadlock-logger 持续采集,比等线上报警再救火要可靠得多。

text=ZqhQzanResources