真正卡住数据库的往往是command='sleep'但trx_state='running'的事务,它已执行完sql却未提交,持行锁、mdl锁和undo段,拖慢purge线程;关键依据是trx_started远早于当前时间且trx_mysql_thread_id与processlist.id匹配。

查 INNODB_TRX 找出“Sleep 却 RUNNING”的事务
真正卡住数据库的,往往不是正在跑 SQL 的连接,而是那种 COMMAND = 'Sleep'、STATE 为空、但 TRX_STATE = 'RUNNING' 的连接。它可能早在几分钟前就执行完一条 SELECT,然后一直挂着事务不提交。
这类连接在 SHOW PROCESSLIST 里看起来无害,但实际持着行锁、MDL 锁、undo 段,还会拖慢 purge 线程。关键判断依据是:TRX_STARTED 时间远早于当前时间,且 TRX_MYSQL_THREAD_ID 能对上 PROCESSLIST.Id。
- 优先执行:
SELECT t.trx_id, t.trx_started, t.trx_state, t.trx_isolation_level, p.ID, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME, p.INFO FROM information_schema.INNODB_TRX t JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE p.COMMAND = 'Sleep' AND t.trx_state = 'RUNNING' ORDER BY t.trx_started; -
TIME > 300且INFO为空 → 极大概率是应用异常中断或忘记COMMIT -
trx_isolation_level = 'REPEATABLE READ'时,哪怕只执行过一条SELECT,事务一启就持 MDL 锁
用 sys.schema_table_lock_waits 确认是否为 MDL 锁元凶
如果业务卡在 DDL(如 ALTER TABLE)或大量 SELECT 集体变慢,别只盯着行锁——Waiting for table metadata lock 是典型 MDL 锁症状,而 sys.innodb_lock_waits 对它完全无效。
sys.schema_table_lock_waits 才是专治元数据锁的视图,它直接暴露 blocking_pid 和 sql_text,后者能告诉你元凶最后执行的是什么语句(比如一个没提交的 SELECT * FROM orders)。
- 先确认采集器已开:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 再查:
SELECT blocking_pid, waiting_pid, sql_text, object_schema, object_name FROM sys.schema_table_lock_waits; -
blocking_pid为NULL并不表示没锁——很可能是隐式事务(BEGIN后只跑了一条SELECT就断开)在持锁
KILL 前必须区分线程 ID 和事务 ID
所有 KILL 操作都作用于连接线程,不是事务本身。TRX_ID 是 InnoDB 内部事务编号,不能用于 KILL;必须用 TRX_MYSQL_THREAD_ID 或 PROCESSLIST.Id。
- 优先执行:
KILL QUERY <code>ID; —— 只中断当前语句,保留连接,避免应用重连风暴 - 慎用:
KILL <code>ID; —— 强制断开连接,可能触发应用重试逻辑或连接池异常 - 如果
blocking_pid查不到对应连接(比如已断连但事务未清理),说明是悬挂事务,需靠innodb_rollback_on_timeout = ON或手动清理information_schema.INNODB_TRX中残留记录
为什么 wait_timeout 调短了还是压不住 Sleep 连接?
MySQL 的 wait_timeout 默认是 28800 秒(8 小时),调成 300 秒后,**只对新连接生效**。已存在的 Sleep 连接仍按旧值倒计时,可能继续挂十几分钟。
更隐蔽的问题在于:应用层用了连接池(如 HikariCP),但没同步配置 idleTimeout 和 maxLifetime。结果是连接池认为连接还健康,MySQL 认为该连接早该超时——两边错位,Sleep 连接就堆起来了。
- 动态调参命令:
SET GLOBAL wait_timeout = 300; SET GLOBAL interactive_timeout = 300; - Java 应用中,
Connection必须显式close(),尤其在异常分支里;try-with-resources是最稳妥写法 -
@Transactional不等于自动释放连接:事务未提交/回滚,连接就一直被持有
真实故障里,一个 Sleep 连接可能同时造成三件事:行锁阻塞后续更新、MDL 锁卡住 DDL、undo 日志持续增长占用磁盘。排查时不能只盯一个表或一个视图,得把 INNODB_TRX、PROCESSLIST、schema_table_lock_waits 串起来看——因为它们反映的是同一场故障在不同层面的投影。











