首页 > 数据库 >MySQL死锁问题解决方案

MySQL死锁问题解决方案

来源:互联网 2026-07-11 08:40:06

MySQL死锁时InnoDB自动回滚代价较小事务并抛出错误码1213,这是正常机制。需通过SHOWENGINEINNODBSTATUS定位冲突SQL及加锁顺序,开启innodb_print_all_deadlocks记录全部死锁。常见根因是SQL执行顺序不一致或索引缺失导致大量行锁冲突。

MySQL发生死锁时,很多人的第一反应是赶紧改代码去。实际上,InnoDB自身能够处理这种情况——它会自动回滚代价较小的事务并抛出错误码1213。这不是故障,而是正常机制在运作,它主动打破了僵局。真正需要做的,是定位哪两条SQL在抢同一组资源,以及加锁顺序为何冲突。

MySQL死锁问题解决方案

长期稳定更新的攒劲资源: >>>点此立即查看<<<

死锁发生后别急着改代码

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 顺序混乱导致的隐蔽冲突——它们不会立刻报错,但会在高并发时突然爆发。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。