查 INFORMATION_SCHEMA.INNODB_TRX 是唯一靠谱的实时手段,该表提供 TRX_STARTED、TRX_STATE、TRX_QUERY 等字段,可精准定位长事务源头,避免依赖 SHOW PROCESSLIST 或慢查询日志的误判。

查 INFORMATION_SCHEMA.INNODB_TRX 是唯一靠谱的实时手段
MySQL 不会把“长事务”单独列成一个状态,它只在内存里维护每个活跃事务的元信息。想确认哪个事务真正在拖后腿,必须查 INFORMATION_SCHEMA.INNODB_TRX 表——它包含 TRX_STARTED(启动时间)、TRX_STATE(当前状态)、TRX_QUERY(正在执行的语句)等真实字段。
常见错误现象:SHOW PROCESSLIST 里看到一堆 State: Locked 或 Waiting for table metadata lock,但找不到源头;或者慢查询日志里没记录任何慢 SQL,应用却卡顿。这时候 PROCESSLIST 只显示等待者,而 INNODB_TRX 才暴露持有锁的事务本身。
- 线上阈值建议设为 10–30 秒,不是等它跑满 60 秒才干预
-
TRX_QUERY为空 ≠ 安全——可能是事务只执行了BEGIN,后续还没发 SQL,但锁已因之前操作持有了 - 别用
SELECT * FROM INNODB_TRX直接扫全表,加 WHERE 条件过滤后再查,避免大表扫描影响性能
用 TIMESTAMPDIFF 算运行时长,别信 TIME_TO_SEC(TIMEDIFF()) 在某些版本的兼容性
计算事务运行秒数,最稳妥写法是:TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW())。这个函数在 MySQL 5.6+ 全版本稳定,返回整型,不依赖会话时区设置。
而 TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) 在部分 5.7 旧补丁版本中会出现精度丢失或 NULL 返回,尤其当事务启动时间跨天时容易出错。
- 监控 SQL 示例(查超 20 秒的事务):
SELECT TRX_ID, TRX_MYSQL_THREAD_ID, TRX_QUERY, TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW()) AS duration_sec FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TIMESTAMPDIFF(SECOND, TRX_STARTED, NOW()) > 20 ORDER BY duration_sec DESC; - 如果要兼容极老版本(如 5.5),可用
UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(TRX_STARTED)替代,但注意它对微秒部分截断 - 别在 WHERE 中直接用函数包裹
TRX_STARTED做范围比较——MySQL 无法走索引(该字段无索引),但这是系统表,影响有限
告警不能只靠脚本轮询,得防误杀和状态延迟
用 shell 脚本 + mysql -e 每 30 秒查一次 INNODB_TRX 并发邮件,看似简单,实则埋雷:事务可能在你 SELECT 和 KILL 之间已提交;权限失效会导致脚本静默失败;更危险的是,KILL CONNECTION 会干掉整个连接,而连接池里一个连接常复用多个逻辑请求。
- 优先用
KILL TRANSACTION <TRX_ID>(MySQL 5.7+ 支持),它只终止事务,保留连接供池复用 - 若必须用脚本自动 kill,请先查
TRX_MYSQL_THREAD_ID,再执行KILL <thread_id>,比拼接TRX_ID更可靠(因TRX_ID在 8.0.29+ 后改为内部事务 ID,不一定对应可 kill 对象) - pt-kill 是生产首选:它内置重试、连接复用、安全过滤,命令如
pt-kill --busy-time 20 --kill --match-state Running --victims all --ignore-user system,上线前务必加--print预览 - 告警触发后别只发消息,同步写入审计表(如
long_trx_log),字段含trx_id、thread_id、duration_sec、query_sample、kill_time,方便回溯
配置层要堵住空闲连接挂长事务的漏洞
很多“长事务”根本不是业务逻辑慢,而是连接空闲着,事务却没提交——比如 ORM 开启了事务,中间调了个 HTTP 接口耗时 5 秒,回来忘了 commit。这时 wait_timeout 就是最后一道防线。
- 设
wait_timeout = 300(5 分钟),作用于非交互式连接(即应用连接池发起的连接) - 设
interactive_timeout = 600(10 分钟),留给 DBA 手动操作空间 - 这两个参数对“已执行 SQL 但未提交”的事务无效,只管“完全没发命令”的空闲连接;修改后旧连接仍按原值计时,新连接才生效
- 配合应用层:所有事务必须显式控制边界,禁止跨 HTTP 请求、跨函数隐式延续;ORM 的
@Transactional或with transaction.atomic():必须配超时兜底(如 Spring 的timeout属性)
真正难防的不是运行 100 秒的事务,而是那个只改一行、但卡在外部调用里不动的 3 秒事务——它不会出现在慢查询日志,INNODB_TRX 里也只显示 TRX_QUERY 为空,但 undo 日志已在悄悄膨胀。监控得盯住 TRX_STARTED 和当前时间差,而不是等它报错才行动。


















