快速确认锁导致卡顿需查show full processlist的state列,大量线程卡在waiting for table metadata lock等状态且time>30s即可判定;再用sys.innodb_lock_waits定位waiting_pid与blocking_pid,重点分析blocking_pid的事务未提交、无索引dml或ddl操作三类高危根源。

如何快速确认是锁导致的卡顿
直接看 SHOW FULL PROCESSLIST 的 State 列:如果大量线程卡在 Waiting for table metadata lock、Waiting for row lock 或长时间停在 updating/deleting 状态(Time > 30s),基本可以断定是锁问题。此时 Info 列里往往能看到重复操作同一张表的 SQL,比如多个 UPDATE orders SET status = ? WHERE id = ? 卡住不动。
查清谁在等、谁在堵:用 sys.innodb_lock_waits
MySQL 8.0 推荐优先查 sys.innodb_lock_waits,它把等待关系扁平化了,比手动 JOIN INNODB_TRX + INNODB_LOCK_WAITS 更快更稳:
SELECT waiting_trx_id, waiting_pid, waiting_query, blocking_trx_id, blocking_pid, blocking_query FROM sys.innodb_lock_waits\G
关键点:
-
waiting_pid是被卡住的连接 ID,可直接KILL它来释放等待(但要先评估业务影响) -
blocking_pid是真正持锁的源头,重点查它的Info和Time—— 如果Time超过几分钟,极大概率是事务没提交、SQL 写错或隐式事务未关闭 - 注意
blocking_query是否为空:为空说明持锁者已执行完 SQL,但事务仍开着(START TRANSACTION后没COMMIT或ROLLBACK)
定位持锁根源的三类高危操作
真正卡死数据库的,往往不是单条慢 SQL,而是以下三类操作引发的锁扩散:
- 未提交的长事务:比如应用层开了事务,中间调用了外部 HTTP 接口或 sleep(5),导致行锁/间隙锁长期不释放
-
无索引的 DML:如
UPDATE users SET deleted = 1 WHERE phone LIKE '%138%',因phone无索引,InnoDB 会升级为全表扫描+全表加锁,瞬间阻塞所有写入 -
DDL 操作:尤其是
ALTER TABLE ... ADD INDEX在大表上执行时,会持有元数据锁(MDL),连SELECT都可能被堵住(状态显示为Waiting for table metadata lock)
紧急止血与后续加固
卡顿正在发生时,优先做两件事:
- 对
blocking_pid执行KILL—— 这是最直接的止血方式,但需确认该连接是否对应关键业务(如支付回调) - 临时降低锁等待超时,避免应用层雪崩:
SET SESSION innodb_lock_wait_timeout = 5;(默认 50 秒,设太低可能导致更多事务回滚)
事后必须检查:持锁 SQL 是否走了索引(用 EXPLAIN)、事务是否被异常中断、是否有 ORM 自动生成的非确定性 SQL(如 where 条件顺序随机导致加锁顺序不一致)。间隙锁(Gap Lock)在 REPEATABLE READ 下极易引发死锁,但不能简单降级隔离级别——得靠业务逻辑收敛加锁范围。











