MySQL 锁等待与死锁进阶:怎么看等待、怎么降冲突(工程化套路)

张开发
2026/4/6 1:37:07 15 分钟阅读

分享文章

MySQL 锁等待与死锁进阶:怎么看等待、怎么降冲突(工程化套路)
目标你能把“锁等待/死锁”从概念背诵提升到线上可落地如何快速判断是锁导致的慢如何拿到等待链与死锁日志如何通过索引与事务改造降低锁冲突1. 先建立直觉慢不一定是 SQL 慢可能是“在等锁”典型现象QPS 下降RT 飙升DB CPU 不高但连接数变多慢 SQL 里耗时很大但执行计划并不差这种很可能是锁等待被别的事务占着1.1 一个可复现的最小例子两条 update 就能制造死锁准备表createtablet_account(idbigintprimarykey,balanceintnotnull);并发执行两段事务注意更新顺序相反事务 T1begin;updatet_accountsetbalancebalance-1whereid1;updatet_accountsetbalancebalance1whereid2;commit;事务 T2begin;updatet_accountsetbalancebalance-1whereid2;updatet_accountsetbalancebalance1whereid1;commit;现象其中一个事务会报 deadlock 并回滚另一个事务继续提交直觉T1 先锁住 id1再等待 id2T2 先锁住 id2再等待 id1形成循环等待2. 锁等待从哪里看三类证据2.1 InnoDB 状态SHOW ENGINE INNODB STATUS能看到 LATEST DETECTED DEADLOCK能看到部分锁等待信息2.1.1 你真正需要从 deadlock 日志里读出 3 件事哪两个事务在互相等待事务 id/线程 id各自持有什么锁、在等什么锁锁类型与索引哪条 SQL 导致的定位到业务入口2.2 performance_schema更体系化能查到等待事件、锁对象、等待时长适合线上持续观测2.3 慢日志与链路慢 SQL 不一定是“执行慢”可能是“等锁慢”需要把“等待时间”从总耗时中拆出来APM 或数据库指标3. 死锁的本质循环等待 InnoDB 主动检测并回滚一方死锁满足A 持有锁 1 等锁 2B 持有锁 2 等锁 1InnoDB 会检测到环并选择回滚“代价较小”的事务通常是修改行少的。4. 常见死锁场景高频且可改造4.1 不同顺序更新同一组资源事务 1先更新 A 再更新 B事务 2先更新 B 再更新 A解决统一资源访问顺序按 id 排序对照组错顺序不一致T1 更新 1-2T2 更新 2-1对所有地方都按 id 从小到大更新1-2或按某个业务维度固定顺序4.2 范围更新导致 Next-Key Lock 覆盖面大update t set ... where idx_col between 10 and 20在 RR 下可能加 next-key 锁导致范围内插入/更新受阻。解决让条件更精确尽量命中唯一键/主键拆小批次减少锁持有时间对照组错一次 update/delete 覆盖大范围事务持续时间长对按主键分批例如每批 200/500每批单独事务提交4.3 二级索引更新 回表锁更新会锁索引记录 对应主键记录如果条件走错索引或扫描范围大会锁很多行冲突飙升。解决用合适索引缩小扫描范围避免在热点表做“大范围 update/delete”对照组错where 条件导致扫描行数大更新锁住大量索引记录与主键记录对补齐合适索引让 where 精确命中减少锁范围与回表5. 降锁冲突的工程方法优先级从高到低5.1 缩短事务时间最有效把 RPC/外部调用移出事务事务内只做必要的 DB 操作避免事务里做复杂计算/循环5.2 缩小锁范围靠索引与写法where 尽量用主键/唯一键让扫描行数最小避免函数/类型转换导致索引失效5.3 拆批处理大批量更新改成分批每批 200/500每批单独事务5.4 统一加锁顺序多行更新时按主键排序多表更新时固定表顺序5.5 降级/重试对幂等操作可做死锁重试带退避对不可重试操作要快速失败并告警6. 线上排查步骤可直接照做确认现象RT 高、连接数高、CPU 不高看慢 SQL是否集中在 update/delete抓 InnoDB status是否有死锁日志查锁等待谁在持锁、谁在等待、等待多久回到 SQL是否扫描过多行EXPLAIN rows是否事务太长是否访问顺序不一致6.1 更流程化的线上排查 checklist从现象到根因先判断“慢”是不是锁等待典型特征CPU 不高、连接数上升、慢 SQL 耗时大但执行计划不差固定证据慢日志拿到 SQL 参数同时抓SHOW ENGINE INNODB STATUS看是否有 deadlock/等待片段找到“谁在持锁、谁在等待”如果能用 performance_schema就用它定位等待链与锁对象回到 SQL 做三类归因事务太长把 RPC/循环/计算移出事务锁范围太大用索引把 where 精确化、减少扫描行顺序不一致统一按主键排序更新、固定多表更新顺序决定临时止血动作低峰再根治降级/限流/拆批幂等更新可加重试带退避7. 面试背诵稿60 秒线上遇到 SQL 慢我会先区分是执行慢还是等锁慢如果 CPU 不高但连接堆积、RT 飙升很可能是锁等待。我会通过SHOW ENGINE INNODB STATUS看死锁日志并结合 performance_schema 查看等待链与持锁事务。降冲突的核心是三点缩短事务时间把 RPC/计算移出事务、缩小锁范围用合适索引让 where 精确命中、减少扫描行数、以及统一加锁顺序避免交叉等待。对于不可避免的死锁可做幂等重试和退避但根因还是要从事务边界、索引和批量操作策略上治理。

更多文章