
MySQL锁等待3分钟定位法从紧急止血到根治的完整排查流程【免费下载链接】CS-Base图解计算机网络、操作系统、计算机组成、数据库共 1000 张图 50 万字破除晦涩难懂的计算机基础知识让天下没有难懂的八股文 在线阅读https://xiaolincoding.com项目地址: https://gitcode.com/GitHub_Trending/cs/CS-Base大促期间一个接口突然集体超时但数据库CPU并不打满。这时候多半不是慢查询而是MySQL锁等待在作怪。本文按「症状自诊 → 三分钟定位 → 先止血后根治 → 预防清单」的完整路径展开读完后你可以照着同样的动线在三分钟内说出锁被谁占着、它为什么占着、下一步该干什么。症状自诊判断语句是被锁卡住的3个信号 排查前先排除另一种可能慢查询是慢在计算锁等待是卡住根本跑不起来。看这3个信号单条语句不慢但卡同一条UPDATE平时毫秒级返回现在跑几十秒期间CPU占用却不涨。它没在干活在排队。PROCESSLIST状态字段执行SHOW PROCESSLIST后看到大量Waiting for table metadata lock、Waiting for row lock状态或者Updating状态长时间不结束。QPS下降、连接数上涨吞吐量掉下去线程数却持续爬升后面的请求被前面的请求一个接一个堵住这是典型的锁等待连锁反应。三条里命中两条基本可以锁定是锁等待直接进入下一步定位。三分钟定位阻塞源头按谁在等 → 谁在占 → 为什么等的动线查 工具不要按列表看要按排查动线串起来全程三步。1. 查谁在等用innodb_lock_waits一次拿到两边执行这条SQL是为了直接拿到等待者 ↔ 阻塞者的配对关系省得自己对着线程号猜-- 谁在等谁一次拿到等待方(blocked)与阻塞方(blocker) SELECT * FROM sys.innodb_lock_waits\G;waiter部分是卡住的语句blocker部分就是持锁事务。把 blocker 的trx_mysql_thread_id抄下来后面止血要用。2. 查谁在占用data_locks看锁到底是什么样上一步知道是谁占这一步要确认占的锁长什么样锁在哪些行、哪些区间上-- 阻塞方身上挂了什么锁锁在哪条记录、哪个区间、什么模式 SELECT * FROM performance_schema.data_locks\G;LOCK_MODE字段是关键X是排他锁REC_NOT_GAP是单行记录锁GAP是纯间隙锁。两者叠加就是 Next-Key 锁可以理解成不光占了座位连旁边的过道也占了。3. 查为什么等从SHOW ENGINE INNODB STATUS的死锁日志里找答案拿一个真实场景说。一张秒杀库存表t_inventorysku_id是普通索引非唯一。开抢瞬间两个不同 sku_id8021、8022的线程并发执行同样的扣减逻辑-- 秒杀扣库存先锁定读库存行再扣减 BEGIN; SELECT id FROM t_inventory WHERE sku_id 8021 FOR UPDATE; UPDATE t_inventory SET stock stock - 1 WHERE sku_id 8021; COMMIT;在 RR 隔离级别下普通索引上的 FOR UPDATE 不只锁住那一行还会锁住周边的区间。假设现有库存行最大到 8020那么 8021 和 8022 的锁范围都会盖住 (8020, ∞) 这段间隙。两个事务各持一段再执行后续写入时就要等对方先放互相持有、互相等待死锁成立。这时执行这条语句是为了拿到死锁日志重点看LATEST DETECTED DEADLOCK段落-- 获取InnoDB状态死锁全貌都在LATEST DETECTED DEADLOCK段里 SHOW ENGINE INNODB STATUS\G;日志里能看到两个事务的ID、各自持有哪把锁、在等哪把锁、最后执行的是什么SQL。锁模式里如果出现GAP或 Next-Key 组合就能确认这是范围锁冲突不是单行竞争。很多MySQL死锁排查起点就是这一段日志。先止血后根治从紧急KILL到索引改造 止血KILL的正确姿势是杀占着的一方不是等着的一方事故当下的第一反应是KILL但杀错了只会让队列多一单。要杀的是持锁的阻塞方不是排队的一方-- 先找到阻塞方的线程号再杀它注意是占着锁的一方不是等待方 SELECT trx_id, trx_mysql_thread_id, trx_query FROM information_schema.innodb_trx WHERE trx_state RUNNING; KILL [thread_id];占着的一方被杀后等待方会自动拿到锁继续执行堆积的队列逐层消化。锁等待超时也别用默认值调小一点快速失败把连接尽早还给连接池而不是挂在锁上干等-- 锁等待超时缩到10秒让业务侧尽早抛错、快速重试 SET GLOBAL innodb_lock_wait_timeout 10;根治索引、事务、隔离级别、业务四个方向止血只解决今天要避免复发得动根源索引设计热表的 WHERE 条件必须走索引全表扫描会把整表每一行都锁上这是多数锁等待风暴的根源。另外幂等校验优先用唯一索引代替SELECT ... FOR UPDATE——上面秒杀的例子sku_id改成唯一索引后FOR UPDATE 就只锁记录本身间隙锁直接消失。事务规范事务要短里面只放数据库操作不掺RPC、文件上传、发短信。跨表时统一访问顺序A表B表和B表A表的顺序交错就是死锁的配方。隔离级别业务允许的话考虑从 RR 降到 READ COMMITTED。RC 下几乎没有间隙锁锁停留在记录级等区间的概率大幅下降。业务改造大事务拆小事务非核心流程通知、积分异步化别让它跟核心流程抢同一把锁。这四个方向也是MySQL性能调优里最划算的四个抓手。InnoDB行锁规则的完整推导可以结合仓库里的 MySQL 有哪些锁 一起看。预防清单上线前必查的5个锁等待隐患 ✅这份清单可以贴在发布流程里每次上线逐项过一遍热表的 WHERE 条件是否都命中了索引全表扫描 全表加锁幂等、去重字段是否建了唯一索引而不是靠SELECT ... FOR UPDATE事务里是否混入了非数据库操作RPC、上传、短信事务要尽量短隔离级别是否还在默认 RR业务上能否放宽到 RCinnodb_lock_wait_timeout是否设为合理值并配了长事务监控告警锁等待只是症状病根是加锁没规划。想继续深挖 InnoDB 的行锁规则和死锁的形成过程仓库里这两篇值得读MySQL 是怎么加锁的、MySQL 死锁了怎么办。【免费下载链接】CS-Base图解计算机网络、操作系统、计算机组成、数据库共 1000 张图 50 万字破除晦涩难懂的计算机基础知识让天下没有难懂的八股文 在线阅读https://xiaolincoding.com项目地址: https://gitcode.com/GitHub_Trending/cs/CS-Base创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考