需联合查询INNODB_TRX与PROCESSLIST定位阻塞源头:长事务持锁不放导致连接池耗尽,通过trx_mysql_thread_id关联可识别“持锁者”与“等待者”,并借助sys.innodb_lock_waits快速定位锁等待链。

查 INNODB_TRX 和 PROCESSLIST 联合定位阻塞源头
连接池耗尽往往不是连接“不够用”,而是大量连接被卡在事务阻塞链里无法释放。MySQL 5.7 中,INFORMATION_SCHEMA.INNODB_TRX 记录事务状态,INFORMATION_SCHEMA.PROCESSLIST 显示连接行为,二者必须联合查——单看 SHOW PROCESSLIST 只能看到“Sleep”或“Locked”,看不出谁在等谁。
典型阻塞场景下,你会看到:
-
PROCESSLIST中多个线程State = 'Locked'或State = 'Waiting for table metadata lock',Time值持续上涨 -
INNODB_TRX中存在trx_state = 'RUNNING'但trx_started时间极早(比如 >60 秒),且trx_rows_modified > 0却未提交 - 这两个表的
trx_mysql_thread_id字段可关联:一个长事务持有锁,其他线程在PROCESSLIST中对应行的Id就是被它阻塞的线程
执行这条语句快速抓出“持锁不放 + 等待堆积”的组合:
SELECT t1.trx_id, t1.trx_started, t1.trx_state, t1.trx_rows_modified,
t2.ID, t2.USER, t2.HOST, t2.DB, t2.COMMAND, t2.TIME, t2.STATE, t2.INFO
FROM INFORMATION_SCHEMA.INNODB_TRX t1
JOIN INFORMATION_SCHEMA.PROCESSLIST t2 ON t1.trx_mysql_thread_id = t2.ID
WHERE t1.trx_state = 'RUNNING' AND TIME_TO_SEC(NOW() - t1.trx_started) > 30;用 sys.innodb_lock_waits 定位锁等待链(5.7 可用)
MySQL 5.7 自带 sys 库(需初始化),其中 sys.innodb_lock_waits 是专为锁诊断设计的视图,它把 INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS 三张底层表做了合理 JOIN,直接暴露“谁在等谁”、“等什么锁”、“已等多久”。
运行以下命令,能立刻看到阻塞链最顶端的事务和所有下游等待者:
SELECT * FROM sys.innodb_lock_waits\G
重点关注字段:
-
waiting_trx_id和blocking_trx_id:明确阻塞关系 -
waiting_pid和blocking_pid:对应PROCESSLIST.Id,可直接KILL -
waiting_query:被堵住的 SQL,常是简单UPDATE或SELECT ... FOR UPDATE -
blocking_query:为空?说明阻塞源是未提交事务本身,而非某条具体语句
注意:sys.innodb_lock_waits 在 5.7 中默认存在,但若未启用 performance_schema 或未安装 sys schema,需先执行 mysql -u root -p mysql < /usr/share/mysql/sys_schema.sql(路径依安装而定)。
监控三类高危事务并自动 KILL(防雪崩关键)
靠人工盯屏来不及。必须用定时脚本或 pt-kill 持续扫描并终止三类事务——它们正是连接池耗尽的直接推手:
- 运行超 60 秒且未提交:
TIME_TO_SEC(NOW() - trx_started) > 60 AND trx_state = 'RUNNING' - 持有锁但连接空闲(
Command = 'Sleep')超 30 秒:需 JOINPROCESSLIST筛选Time > 30 - 已修改超 10 万行仍未提交:
trx_rows_modified > 100000(配合SHOW ENGINE INNODB STATUS的 TRANSACTIONS section 验证是否真在刷数据)
示例脚本逻辑(伪 SQL):
SELECT CONCAT('KILL ', trx_mysql_thread_id, ';')
FROM INFORMATION_SCHEMA.INNODB_TRX
WHERE TIME_TO_SEC(NOW() - trx_started) > 60
AND trx_state = 'RUNNING'
AND trx_rows_modified < 100000;生成 KILL 命令后,务必加 --sleep 间隔执行,避免批量 KILL 引发瞬时压力;生产环境建议先 SELECT 出来人工确认,再批量执行。
别漏掉 MDL 锁和隐式长事务
很多连接池耗尽案例,根源不在行锁,而在元数据锁(MDL)。比如 ALTER TABLE、CREATE INDEX 或慢 SELECT 正在读大表,会持 MDL 锁,导致后续所有 DDL/DML 全部排队——这时 PROCESSLIST 状态全是 Waiting for table metadata lock,但 INNODB_TRX 里可能找不到对应事务。
查 MDL 锁最快方式:
SELECT * FROM sys.schema_table_lock_waits\G
此外,还要警惕“隐式长事务”:应用代码里 autocommit = 0 后执行了 SELECT,没显式 BEGIN,但事务已开启;之后忘了 COMMIT,这个“只读事务”照样持锁、拖慢 purge、卡住连接池。这类事务在 INNODB_TRX 中 trx_rows_modified = 0,容易被忽略,但 trx_started 时间异常早就是线索。
真正难处理的,永远是那种既不报错、也不活跃,就静静挂着的 Sleep 连接——它背后可能是一个忘了 ROLLBACK 的 try-catch 块,也可能是一次失败的 RPC 调用后遗留的事务上下文。这类问题不会出现在慢查询日志里,只能靠持续监控 trx_started 和 PROCESSLIST.Time 的差值来揪出来。


















