一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何处理MySQL数据库中的死锁问题

时间:2026-07-10 10:53:52 编辑:袖梨 来源:一聚教程网

MySQL死锁发生后无需立即改代码,InnoDB会自动回滚代价较小事务并报错1213;应优先执行SHOW ENGINE INNODB STATUSG定位LATEST DETECTED DEADLOCK段,比对两事务SQL、HOLDS/WAITING锁信息及lock_mode类型,结合innodb_print_all_deadlocks开启全量日志分析,根因多为加锁顺序不一致或索引失效。

死锁发生后别急着改代码

MySQL 检测到死锁会自动回滚代价较小的事务,并抛出 Deadlock found when trying to get lock 错误(错误码 1213)。这不是故障,是 InnoDB 正常工作的表现——它主动打破了僵局。真正要做的,是定位哪两条 SQL 在抢同一组资源,以及为什么它们加锁顺序冲突。

SHOW ENGINE INNODB STATUSG 提取现场信息

这是排查死锁最直接、不可跳过的一步。执行后,在输出中只看 LATEST DETECTED DEADLOCK 段落,重点关注三块:

  • 每个事务的 query id 后紧跟着的实际 SQL,比如 UPDATE t SET c = c + 1 WHERE id = 2
  • 事务 (1) 的 HOLDS THE LOCK(S) 和事务 (2) 的 WAITING FOR THIS LOCK TO BE GRANTED —— 这告诉你谁先占了什么、谁在等什么
  • 锁类型描述,如 RECORD LOCKS lock_mode X locks rec but not gap 表示主键上的排他记录锁;若出现 gapnext-key,说明涉及间隙锁,大概率和范围查询或非唯一索引有关

查不到日志?确认 innodb_print_all_deadlocks 是否开启

默认情况下,SHOW ENGINE INNODB STATUS 只保留最近一次死锁。如果线上频繁报错但日志里找不到上下文,大概率是没开全量记录。

  • 临时开启(重启失效):SET GLOBAL innodb_print_all_deadlocks = ON;
  • 永久生效需写入配置文件:[mysqld] 下加 innodb_print_all_deadlocks = 1,并确保 log_error 路径可写
  • 注意:开启后所有死锁都会追加进 error log,不清理可能撑爆磁盘

SQL 执行顺序不一致是最常见根因

很多团队把死锁归为“偶发”,其实只要两个事务更新同一组行但顺序不同,就一定会在足够高的并发下复现。

  • 典型例子:
    事务 A:UPDATE orders SET status=2 WHERE id=100;UPDATE users SET last_order_id=100 WHERE id=5;
    事务 B:UPDATE users SET last_order_id=101 WHERE id=5;UPDATE orders SET status=2 WHERE id=101;
  • 解决方式不是加重试,而是强制统一操作顺序:比如永远先 orders,再 users
  • 索引缺失也会放大问题:WHERE user_id = ?user_id 无索引,InnoDB 会全表扫描并锁大量行,极易与其他事务交叉冲突

真正难处理的不是单次死锁,而是那些因间隙锁、长事务或 ORM 自动生成 SQL 顺序混乱导致的隐蔽冲突——它们不会立刻报错,但会在高并发时突然爆发。

热门栏目