Skip to content
数据库与网关故障排查 · 第 2 篇 / 共 4 篇
领域生产问题
专题数据与基础设施排障专题
当前序列数据库与网关故障排查
阅读位置第 2 篇 / 共 4 篇当前专题第 1 个序列 / 共 5 个序列

数据库死锁和锁等待超时案例怎么复盘

数据库锁冲突类问题很容易把团队带进一种状态:

  • 大家都知道“发生了死锁”
  • 也知道“有锁等待超时”
  • 但真正问到是哪两条 SQL、哪两个事务、为什么按这个顺序执行时,就说不清了

这类问题最怕的误区是:

  • 只会调大超时
  • 只会加重试
  • 只会说“数据库有点慢”

这些动作顶多缓一阵,不能解决真正的并发冲突结构。

先说结论

  • 复盘死锁和锁等待,关键不是只知道“谁报错了”,而是重建两个或多个事务的执行顺序
  • 第一目标是先分清这是死锁、普通锁等待,还是慢 SQL 导致的长事务占锁
  • 第二目标是找到持锁点、等待点和访问顺序是否一致
  • 很多锁问题本质不是数据库参数,而是事务边界过大、索引不命中、更新顺序不一致

一个典型故障现场

一个很典型的场景是订单系统:

  • 应用日志里开始出现 deadlock
  • 一部分请求直接失败
  • 另一部分请求长时间卡住,最后 lock wait timeout
  • MySQL CPU 并不高,但接口 RT 已经明显抖动

这时如果只看“数据库响应慢”,很容易漏掉真正关键信息:

  • 事务 A 先锁了表/索引上的哪一部分
  • 事务 B 又锁了哪一部分
  • 后面它们是怎样互相等待的
  • 为什么这次才冲突,以前没有

第一阶段:先止血,避免冲突放大

1. 先确认失败范围和业务影响

要先分清:

  • 是单个接口冲突
  • 还是一组核心交易流程都在冲突
  • 是偶发尖刺
  • 还是持续复现

2. 必要时先降并发或切掉批量任务

很多锁冲突不是纯线上流量触发,而是:

  • 定时任务批量更新
  • 补偿任务扫表
  • 大事务回写
  • 运营后台批量操作

如果这些动作正在放大冲突,先停掉或降速,往往比继续盯数据库监控更有效。

3. 谨慎使用应用侧重试

死锁场景下,有限重试有时是合理的;但如果锁等待本来就严重,放大重试只会把数据库压得更乱。

第二阶段:先分清是死锁还是锁等待超时

1. 死锁

特点通常是:

  • 两个或多个事务形成循环等待
  • 数据库会主动回滚其中一个事务
  • 应用收到 deadlock 报错

2. 锁等待超时

特点通常是:

  • 一个事务一直等另一个事务释放锁
  • 没形成循环
  • 但等太久后超时失败

3. 长事务导致的“看起来像死锁”

有些问题并没有真正形成死锁,只是某个事务长时间持锁,后面一串请求都在排队。

先把这三类分清,后面的分析才不会跑偏。

第三阶段:复盘时最该还原的四件事

1. 哪几个事务参与了冲突

不是只看“哪个接口报错”,而是要看冲突链路里每个事务分别在做什么。

2. 它们按什么顺序访问数据

死锁最经典的根因之一就是访问顺序不一致:

  • 事务 A 先改订单,再改库存
  • 事务 B 先改库存,再改订单

并发一高,就很容易互相卡住。

3. SQL 有没有命中合适索引

没有命中索引时,锁范围往往会比你以为的大得多。

这也是很多“明明只改一行,为什么锁了一片”的根因。

4. 事务边界是不是太大

有些事务里混了:

  • 远程调用
  • 复杂计算
  • 多次查询和更新
  • 甚至用户态等待

结果锁持有时间被大幅拉长。

常见根因画像

1. 更新顺序不一致

这是死锁最典型的结构性根因。

2. 大事务或长事务

事务时间一长,锁冲突概率和影响范围都会明显放大。

3. 索引缺失或条件不精确

让更新、删除、扫描时锁住更多记录或更大范围。

4. 批量任务和在线请求抢同一批数据

非常常见,也最容易在高峰时突然爆。

5. 业务逻辑把“查 + 改 + 再查 + 再调外部接口”都塞进一个事务

这类设计在并发低时可能没问题,但一上量就容易放大锁问题。

一个更实用的复盘顺序

遇到这类问题,我通常按这个顺序复盘:

  1. 先确认报错类型,是 deadlock 还是 lock wait timeout。
  2. 再抓当时参与冲突的 SQL 和事务链路。
  3. 还原它们的访问顺序和持锁顺序。
  4. 看 SQL 是否走了正确索引,锁范围是否被意外放大。
  5. 看事务边界里有没有不必要的长逻辑。
  6. 最后再决定是调整 SQL、拆事务、统一更新顺序,还是改批量任务策略。

这样复盘出来的结论更容易真正指导后续改造,而不是只停在“数据库偶发冲突”。

止血后的治理方向

1. 统一核心资源访问顺序

尤其是订单、库存、账户、余额这类热点资源。

2. 缩小事务边界

能移出事务的逻辑尽量移出,别把远程调用和非关键步骤放进事务里。

3. 确保更新条件命中索引

避免扩大锁范围。

4. 批量任务错峰和限速

别让补偿、归档、批量变更和在线核心交易长期抢同一批资源。

总结

数据库死锁和锁等待超时真正要复盘的,不只是“谁报错了”,而是哪些事务在什么顺序下抢了哪些资源。

把“事务顺序、SQL 索引、锁范围、事务边界”这几条线理顺之后,死锁和锁等待问题通常都会从偶发故障,变成能被持续治理的结构性问题。

延伸阅读相关文章优先当前专题,再补跨专题关联。
同一序列 · 顺着当前主线继续读RabbitMQ 队列积压时怎么排查适合把 K8s、RabbitMQ 和数据库锁等待问题放在一条故障治理主线上看。数据与基础设施排障专题 · 基础设施与中间件故障案例同一序列 · 回看前文会更完整Kubernetes CrashLoopBackOff 时怎么排查适合把 K8s、RabbitMQ 和数据库锁等待问题放在一条故障治理主线上看。数据与基础设施排障专题 · 基础设施与中间件故障案例同专题其他序列 · 共享标签:MySQL、线上排障MySQL 连接打满时要先判断哪几类来源适合把 Redis 抖动、MySQL 连接打满、MQ 积压、ES 发黄、ClickHouse 合并堆积和磁盘打满放在同一条基础设施排障主线上看。数据与基础设施排障专题 · 数据与基础设施故障的分层排查同专题其他序列 · 共享标签:MySQL、线上排障MySQL 连接数打满时怎么排查适合把数据库连接、死锁、网关异常和磁盘空间问题放在一条排障线上看。数据与基础设施排障专题 · 数据库与网关故障排查跨专题关联 · 共享标签:并发、线上排障线程池打满时怎么排查适合把 CPU、Full GC、线程池和接口超时这类应用层异常放在一组里看。应用运行时排障专题 · 应用运行时异常排查跨专题关联 · 共享标签:并发、线上排障MySQL 死锁怎么排查适合把事务锁、死锁、主从复制、写后读一致性和切换治理串起来看。MySQL 专题 · MySQL 事务、锁与高可用
继续阅读数据库与网关故障排查当前序列第 2 篇 / 共 4 篇当前专题第 1 个序列 / 共 5 个序列
往前看
上一篇MySQL 连接数打满时怎么排查回到当前序列上一章上一序列应用运行时异常排查从第 1 篇开始:CPU 飙升时怎么排查
往后看
下一篇Nginx 502 怎么排查继续当前序列下一章下一序列缓存、消息与检索故障排查从第 1 篇开始:Redis 热 Key 突增时怎么止血和回查

把零散经验整理成可查、可复用、可持续更新的企业级知识门户