应优先查INNODB_LOCK_WAITS和INNODB_TRX定位阻塞源:blocking_pid对应持锁事务,其TRX_STATE='RUNNING'且TRX_STARTED远早于当前时间即为元凶;仅看PROCESSLIST中Time最大者易误判受害者。

直接查 INNODB_LOCK_WAITS + INNODB_TRX,别只盯着 SHOW PROCESSLIST 里 Time 最大的那个——它大概率是“等得最久的受害者”,不是“占着不放的阻塞源”。
为什么不能只看 PROCESSLIST 的 Time 列
SHOW PROCESSLIST 的 Time 是线程处于当前状态的秒数,对 State = 'Waiting for row lock' 的线程来说,这个数字只是它排队的时间,不代表它在干活。真正该揪的是那个 State = 'Sleep'、Time > 300、但 TRX_STATE = 'RUNNING' 的事务线程——它早就执行完了 SQL,却没提交,锁还挂着。
-
TRX_QUERY为空 +TRX_STARTED超过 1 分钟 → 极大概率是空挂事务 - 同一
User和Host下多个线程Time持续增长 → 可能是同一个上游事务阻塞了整条链 -
Command = 'Sleep'且Time很大,但INNODB_TRX里仍显示TRX_STATE = 'ACTIVE'→ 应用端未正确关闭事务
用 INNODB_LOCK_WAITS 快速定位 blocking_pid
MySQL 8.0+ 直接查 sys.innodb_lock_waits 最省事;5.7 用联查语句。关键不是“谁在等”,而是“谁被它挡着”:
SELECT waiting_pid, blocking_pid, waiting_query, blocking_query FROM sys.innodb_lock_waits\G
-
blocking_pid对应的就是要KILL的线程 ID,不是waiting_pid -
blocking_query为空 ≠ 安全,可能刚执行完UPDATE就卡住,事务没结束 - 如果
blocking_pid查不到对应活跃线程,去PROCESSLIST里找Command = 'Sleep'且Time > 600的 ID,八成就是它
确认阻塞源是否真在持锁:查 TRX_STARTED 和锁范围
拿到 blocking_pid 后,必须验证它是否真的长期持锁,而不是误判:
SELECT trx_id, trx_started, trx_state, trx_query, trx_mysql_thread_id FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_mysql_thread_id = ?
-
trx_state = 'RUNNING'且trx_started早于当前时间 5 分钟以上 → 高风险长事务 - 隔离级别是
REPEATABLE-READ时,WHERE id > 100 AND id < 200这类范围条件会加间隙锁,阻塞插入,别只盯着等值更新 - 非唯一索引等值查询(如
WHERE name = 'xxx')也会锁多行,EXPLAIN 显示走索引 ≠ 只锁一行
KILL 前务必核对线程身份和影响面
杀错线程可能引发主从延迟或数据不一致,尤其注意以下两类:
-
User = 'system user'的线程 → 主从同步线程,绝对不要 KILL -
Host是内部服务 IP(如10.10.1.5:34291)且DB是核心业务库 → 先联系对应服务负责人,确认是否可中断 - 用
KILL QUERY [thread_id]先试探:只中断当前语句,不回滚整个事务,适合调试阶段
真正难的不是找到 blocking_pid,而是判断它背后是不是一个没设超时的 RPC 调用、一个被 catch 住却没 rollback 的异常分支,或者一个前端点了提交按钮后网络断开导致的“半截事务”。这些不会留在 SQL 层面,得倒查应用日志。


















