查innodb_trx可快速定位长时间未提交事务,重点关注trx_started时间早于当前30分钟且trx_state为running或lock wait的记录,结合processlist确认线程状态,trx_mysql_thread_id为kill关键id,需区分真实运行事务与僵尸事务并谨慎处理。

查 INFORMATION_SCHEMA.INNODB_TRX 看长时间未提交的事务
MySQL 的 INNODB_TRX 表会实时记录所有活跃的 InnoDB 事务,其中 TRX_STATE 为 LOCK WAIT 表示正卡在锁上,而 TRX_STARTED 时间过早(比如 > 30 分钟)但状态仍是 RUNNING 或 LOCK WAIT 的,大概率是应用崩溃、连接异常断开后遗留的僵尸事务。
执行以下查询快速定位可疑事务:
SELECT TRX_ID, TRX_MYSQL_THREAD_ID AS thread_id, TRX_QUERY, TRX_STATE, TIMEDIFF(NOW(), TRX_STARTED) AS duration, TRX_WAITING_LOCK_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_STARTED <p>注意:<code>TRX_MYSQL_THREAD_ID</code> 是 kill 操作所需的关键 ID;<code>TRX_QUERY</code> 可能为 <code>NULL</code>(比如事务只执行了 BEGIN 未发 SQL),不能仅凭它判断是否“有操作”。</p><h3>结合 <code>INFORMATION_SCHEMA.PROCESSLIST</code> 判断连接是否已失效</h3><p>一个事务存在,但对应连接可能早已断开(比如客户端 crash、网络中断、超时关闭),这时 <code>PROCESSLIST</code> 中该线程状态会是 <code>Sleep</code> 且 <code>COMMAND</code> 为 <code>Sleep</code>,但 <code>TIME</code> 值极大(远超 wait_timeout),或 <code>INFO</code> 为空 —— 这类线程无法响应 KILL,却仍持有锁。</p><p>关联查询确认:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill2334" title="MySQL"><img src="https://img.php.cn/upload/skill/000/000/081/178900927846657.jpg" alt="MySQL" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="overflowclass">MySQL</a> <p class="overflowclass">编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。</p> </div> <a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><pre class="brush:php;toolbar:false;">SELECT t.TRX_ID, p.ID AS processlist_id, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME AS process_time, p.STATE, p.INFO FROM INFORMATION_SCHEMA.INNODB_TRX t LEFT JOIN INFORMATION_SCHEMA.PROCESSLIST p ON t.TRX_MYSQL_THREAD_ID = p.ID WHERE t.TRX_STARTED <p>重点关注:<code>COMMAND</code> 是 <code>Sleep</code> 且 <code>process_time > 300</code>(5 分钟),同时 <code>STATE</code> 为空或为 <code>Waiting for table flush</code> 等非活跃态 —— 这类基本可判定为僵尸。</p><h3>KILL 之前务必确认是否真可终止</h3><p>直接 <code>KILL <thread_id></thread_id></code> 对某些僵尸事务无效(尤其连接已断),但对仍在运行的长事务又可能引发回滚风暴。必须分情况处理:</p>
- 如果
PROCESSLIST中对应线程状态是Query且INFO显示真实 SQL(如UPDATE ... WHERE ...),说明它还在跑,KILL 后会触发回滚 —— 先评估该 SQL 影响行数和回滚耗时 - 如果线程状态是
Sleep且TIME > wait_timeout(默认 28800 秒),MySQL 本该自动清理,未清理说明可能被阻塞在锁链中,此时 KILL 通常无响应,需从上游查起(比如是否有未提交的分布式事务、XA 事务残留) - 若
TRX_STATE = 'LOCK WAIT'且TRX_WAITING_LOCK_ID非空,用INFORMATION_SCHEMA.INNODB_LOCK_WAITS查谁在持锁 —— 僵尸事务往往不是锁源,而是锁受害者
预防比检测更重要:应用层必须设事务超时
MySQL 本身不提供“事务级自动超时”机制(innodb_lock_wait_timeout 只控制锁等待时间,不杀事务本身),所以僵尸事务几乎全是应用侧问题:未捕获异常、未写 finally rollback、连接池配置不当等。
关键落地点:
- 所有显式
BEGIN后必须配对COMMIT或ROLLBACK,禁止裸写BEGIN - 应用代码里给数据库操作加业务级超时(如 Spring 的
@Transactional(timeout = 30)) - 连接池(如 HikariCP)开启
leakDetectionThreshold,及时发现未关闭的连接 - 定期跑脚本(如每 5 分钟)扫描
INNODB_TRX中TRX_STARTED 的事务并告警,而不是等锁等待爆发
真正难处理的不是“怎么查”,而是当 TRX_STATE 显示 RUNNING 却 PROCESSLIST 找不到对应线程时——那说明内核态事务结构还在,但用户态连接彻底消失了,这种只能重启 mysqld 或等待其自行释放(取决于 innodb_fast_shutdown 设置)。










