当前位置: 首页 > news >正文

数据库崩了别只会上香:死锁日志里藏着的4条“凶手线索”

大家好,这里还是小耶。

今天聊一个让我半夜爬起来好几次的话题:数据库死锁。

前阵子有个读者跟我描述了一个场景:某个电商大促期间,订单系统突然大面积报Deadlock found,业务方快疯了。他赶紧执行了SHOW ENGINE INNODB STATUS,看到LATEST DETECTED DEADLOCK下面一大段日志,每个字母都认识,串起来完全看不懂。CPU 一度飙到 70%,所有 update/insert 都在排队等锁释放。

死锁的可怕之处在于:系统内有 4 万毫秒的强制锁释放超时设置,这意味着单个锁的等待最多 40 秒。一旦上万个请求同时堆积,不仅是数据库,整个应用层在 40 秒内都会体验断崖式响应延迟。如果是核心交易系统,用户看到的就是白屏和“网络异常”。

死锁到底是什么?

先说明白这个问题。

死锁,是指两个或多个事务在执行过程中,因争夺同一资源而互相等待,结果谁也无法继续执行,形成一个循环等待的僵局。事务 A 拿着资源 1 在等资源 2,事务 B 拿着资源 2 在等资源 1,谁也等不到,最后只能被数据库内核强制中止其中一个。死锁通常发生在使用 InnoDB 行锁且并发较高的事务中。

要让死锁发生,必须同时满足四个条件:

  • 互斥​:资源不能被共享,一次只能被一个事务占用
  • 持有并等待​:事务已持有一部分资源,同时还在等待其他事务持有的资源
  • 非抢占​:资源不能被强行剥夺,必须由持有的事务主动释放
  • 循环等待​:事务之间形成了 A 等 B、B 等 A 的闭环

理论上打破其中任何一个,死锁就不会发生。但在高并发业务中,这四者几乎会同时出现。

InnoDB 在解决死锁时会做什么?

InnoDB 默认死锁检测是开启的。一旦检测到死锁,会自动回滚其中一个事务(通常选择执行成本较小或持有锁较少的事务来作为牺牲者),另一个事务继续执行。这就是为什么Deadlock found错误往往是偶发性的——有时候死锁触发了,其中一个事务被回滚,报错出现;有时候刚好绕过去了,业务不受影响。但如果业务本身对事务提交失败没有重试机制,每一次死锁都是一个定时炸弹。

SHOW ENGINE INNODB STATUS 完整解读

我第一次看到这个命令的输出,满屏的十六进制地址、锁结构体,完全看不懂。后来在线上一遍遍对日志、查文档,才慢慢啃下来。今天把精华拆给你看。

SHOW ENGINE INNODB STATUS返回的内容很长,只会有一次最近的死锁记录(如果发生过多次,也只会保存最后一次),所以运维上建议开启innodb_print_all_deadlocks = ON,把所有死锁日志记录进错误日志,方便长期追踪。

下面是一个典型死锁日志的核心结构拆解:

text

------------------------ LATEST DETECTED DEADLOCK ------------------------ 2026-01-15 14:23:45 0x7f8a2c001700 *** (1) TRANSACTION: TRANSACTION 310298, ACTIVE 0 sec fetching rows mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s) MySQL thread id 12345, OS thread handle 14000, query id 67890 10.0.0.1 app_user updating UPDATE orders SET status = 'PAID' WHERE order_id = 10086 *** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 100 page no 3 n bits 72 index PRIMARY of table `db`.`orders` trx id 310298 lock_mode X locks rec but not gap *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 100 page no 5 n bits 72 index idx_status of table `db`.`orders` trx id 310298 lock_mode X locks gap before rec insert intention waiting *** (2) TRANSACTION: TRANSACTION 310299, ACTIVE 0 sec fetching rows ... *** (2) HOLDS THE LOCK(S): ... *** (2) WAITING FOR THIS LOCK TO BE GRANTED: ... *** WE ROLL BACK TRANSACTION (1)

逐行解读:

  • ​**TRANSACTION​行**​:ACTIVE 0 sec表示当前事务已经活跃 0 秒,说明死锁在短时间内就被检测到了。
  • ​**mysql tables in use 1, locked 1**​:事务用了几张表,锁了几张表。
  • ​**LOCK WAIT**​:当前事务正在等待锁。
  • ​**HOLDS THE LOCK(S)**​:关键部分。表示当前事务已经拿到了哪些锁。这里lock_mode X表示排他锁,locks rec but not gap表示行锁(记录锁),没有锁住间隙。看到gap before recinsert intention时,需要警惕间隙锁——很多死锁都源于间隙锁与插入意向锁的冲突。尤其是在 MySQL 默认的REPEATABLE READ隔离级别下,间隙锁范围会被扩大,死锁概率大增。
  • ​**WE ROLL BACK TRANSACTION (1)**​:谁被选中作为牺牲者回滚了。可以据此调整事务顺序,让不关键的 SQL 成为被回滚的一方。

补充工具:Performance Schema

从 MySQL 8.0 开始,官方推荐使用 Performance Schema 来更精细地监控锁竞争:

sql

-- 查看当前所有锁信息 SELECT * FROM performance_schema.data_locks; -- 查看当前锁等待关系 SELECT * FROM performance_schema.data_lock_waits; -- 结合当前事务状态一起分析 SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id FROM information_schema.INNODB_TRX WHERE trx_state = 'LOCK WAIT';

这些视图能看到死锁发生前实际的阻塞链条,比SHOW ENGINE INNODB STATUS的被动后置分析更主动。

常见死锁场景与解决方案

1. 多表访问顺序不一致(最常见)
事务 A:UPDATEt1→ UPDATEt2;事务 B:UPDATEt2→ UPDATEt1。解法:统一规定业务代码中的访问顺序,按表名或主键 ID 升序访问。

2. 批量更新时的死锁
SQL 执行计划异常导致全表扫描,一次性锁了大量行。解法:在 WHERE 条件上建立合适的索引,缩小扫描范围。

3. 并发插入时的间隙锁冲突
RR 隔离级别下,InnoDB 会锁住间隙防止幻读,多个事务同时插入时可能互相等待。解法:考虑降级为READ COMMITTED隔离级别(RC 模式无间隙锁),同时 binlog 格式设置为 ROW。

4. 外键约束引发的死锁
更新主表时不仅要锁本表,还要检查关联子表,高并发下容易死锁。

一点个人体会

我是运营转行做的 DBA。早期处理故障时,总习惯先看监控大盘、重启应用、发公告——这是运营的“止血思维”。后来被线上问题教育了几次才明白,DBA 得反过来:先看锁日志、定位具体 SQL、再决定怎么处理。盲目重启只是掩盖问题,过不了多久还会再崩。这个思维转换,比学会任何一条命令都重要。

死锁预防的最佳实践清单

  1. 统一加锁顺序​:所有事务严格按相同顺序访问表和行。
  2. 拆分长事务​:事务越短越好,避免在事务中调用外部 API 或做耗时操作。
  3. 建好索引​:让 WHERE 条件筛选尽可能少的行,降低锁冲突范围。
  4. 考虑降隔离级别​:业务允许时使用READ COMMITTED,消除间隙锁导致的死锁。
  5. 添加重试机制​:应用层捕获Deadlock found后重试,往往能绕过瞬时的锁竞争。

总结

死锁排查的核心方法:先用SHOW ENGINE INNODB STATUS确认死锁及涉及 SQL,然后分析锁等待链,找出形成循环的两个事务,接着排查索引是否生效、是否存在全表扫描,最后从代码层面统一访问顺序、拆分长事务、完善重试。

死锁不是玄学,它是数据库并发控制的必然产物。理解它、排查它,是 DBA 和普通开发的分水岭。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽……我们下次见~

参考文献

  1. MySQL官方文档:《InnoDB Deadlocks》
  2. MySQL官方文档:《SHOW ENGINE INNODB STATUS Reference》
http://www.jsqmd.com/news/859886/

相关文章:

  • OpenRPA完全指南:免费开源的企业级RPA自动化终极方案
  • CPT Markets:客户体验持续优化的系统思维
  • 介绍 NGINX 中的 Agentic Observability:实时 MCP 流量监测
  • Taotoken的TokenPlan套餐如何让我的开发成本更可控
  • 以 AIGC 贯通设计 — 生产 — 营销:集之互动推动服装电商供应链进入全域协同新阶段
  • 618+毕业季:学生游戏本怎么选?天选6 Pro等4款一线全推荐
  • LDDC:免费歌词工具终极指南 - 如何快速获取精准卡拉OK歌词
  • 四川高考 470-530 分之间,报考重庆哪所高校好?(2026 靠谱学校推荐) - 品牌2025
  • 终极解决方案:在Chrome浏览器中实现密码无缝同步
  • 2026年腾讯云OpenClaw/Hermes Agent配置Token Plan全步骤操作指南
  • Cosy通过USB连接PLC时如何排查USB over IP故障
  • [特殊字符] 视觉Transformer (ViT) 原理及性能突破:从CNN到大规模自注意力机制的迁移
  • 2026年6月PMP最后14天自救指南:说实话,现在不是你放弃的时候
  • 2026年Q2中国化粪池清理服务优质企业首选推荐:合肥玉通管道工程有限公司安徽砼威建材有限公司 - 安互工业信息
  • 【抽奖系统-0】Redis 缓存与 RabbitMQ 削峰实战;架构梳理
  • 随机数Random
  • 当行李箱开始追着你跑:我拆解了自动跟随技术为什么在2026年突然爆发
  • OpenClaw集群有哪些应用场景?
  • 美联储加息降息,如何牵动美黄金价格?
  • 工业视频应用进入“超高速时代”:100Gbps背后意味着什么?
  • SGLang 与 vLLM 对比评测:谁更适合你的生产环境?
  • 2026年AI写作辅助平台盘点:12款神器助你高效完成文献搜集、创作和修稿
  • 饮品防窜货系统:数字化管控,筑牢渠道秩序防线 附联系方式 - 易全一物一码提供商
  • CS/β-GP/nmTiO2温敏声敏双控可注射水凝胶的相变行为
  • ElevenLabs四川话语音落地避坑清单:97%开发者忽略的3个方言声学参数校准关键点
  • C# DateTime操作全解析
  • 2026年4月口碑好的彩钢墙板源头工厂口碑推荐,铝制地板/PVC防静电地板/硫酸钙地板/静电地板,彩钢墙板门店找哪家 - 品牌推荐师
  • MySQL 8.0 vs 国产数据库 vs PostgreSQL:索引特性全面对比
  • Unity UI适配方案
  • RPC 核心概念 03:序列化与传输协议