最准方法是查 information_schema.innodb_trx 表,trx_state=running 但 trx_started 很早即为长事务;再关联 processlist 查线程状态和 info,确认是否漏提交或卡慢查询。

怎么查出哪个连接卡着没提交事务
长事务不提交,innodb_trx 表里就一直挂着,trx_state 是 RUNNING,但 trx_started 时间老早,说明它早就开始、却迟迟没 COMMIT 或 ROLLBACK。直接查这个表最准。
- 运行
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 5;,重点关注trx_mysql_thread_id(对应PROCESSLIST的 ID)、trx_started、trx_state和trx_query - 再连查
information_schema.PROCESSLIST,用trx_mysql_thread_id关联ID,看USER、HOST、COMMAND、TIME和实际执行的INFO,确认是不是应用端漏了提交,还是 SQL 卡在慢查询上 - 注意:如果
trx_query是NULL,不代表没在干活——可能正在执行大更新、锁等待,或刚执行完语句但还没发COMMIT
为什么 SHOW PROCESSLIST 看不到长事务的 SQL
SHOW PROCESSLIST 只显示当前正在执行的语句;一旦语句执行完、事务还开着,INFO 字段就变 NULL,但线程仍占着连接、事务状态仍在 innodb_trx 里持续存在。这时候只看 PROCESSLIST 容易误判“没事了”,其实日志还在涨。
- 务必结合
information_schema.innodb_trx和PROCESSLIST交叉验证,不能只依赖后者 -
TIME字段在PROCESSLIST里是线程空闲时间(比如等应用发下一条命令),不是事务持续时间;真正看事务时长得盯trx_started - 某些 ORM(如 Django 的
atomic块、Spring 的@Transactional)会在异常时自动回滚,但如果捕获了异常又没显式处理事务,就容易留下“静默挂起”的事务
innodb_log_file_size 调大能缓解日志膨胀吗
不能。日志文件大小只影响单个日志文件的容量上限和 checkpoint 频率,跟“未提交事务产生的日志量”没有直接关系。只要事务不提交,不管日志文件多大,ib_logfile0/1 就会不断循环覆盖——但覆盖的前提是这些日志已被 checkpoint 刷到数据页。而长事务会拖住 checkpoint,导致日志无法复用,最终填满整个日志空间,触发 Cannot allocate log record 错误。
- 调大
innodb_log_file_size只是买了点缓冲时间,治标不治本 - 真正要做的:定位并终止源头的长事务,或让应用逻辑确保事务边界清晰(比如避免在事务里做 HTTP 请求、文件读写等外部耗时操作)
- 如果频繁出现,检查是否开了
autocommit=0且没手动控制,或者用了SET SESSION TRANSACTION ISOLATION LEVEL ...后忘了START TRANSACTION就直接跑 UPDATE
安全清理长事务连接的实操步骤
别一上来就 KILL,先确认影响范围。尤其要避开主从复制延迟高、或事务正在被 binlog dump 线程读取的场景。
- 用
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE ID = ?;再确认一次目标连接当前状态 - 如果
STATE是Locked或Waiting for table metadata lock,KILL可能卡住,需先查performance_schema.metadata_locks(MySQL 5.7+)或INNODB_LOCK_WAITS找阻塞源 - 执行
KILL ?后,立刻查innodb_trx确认该trx_mysql_thread_id是否消失;若还在,说明事务处于“回滚中”,此时不可重复KILL,只能等它自己完成(回滚可能比提交更慢) - 清理后观察
SHOW ENGINE INNODB STATUS\G中LOG段的Log sequence number和Last checkpoint at差值是否快速收窄——这是日志空间真正释放的信号
事务没提交,日志就不会被 checkpoint 推进,这点最容易被忽略。监控不能只看磁盘空间,得盯住 innodb_trx 里的活跃时长和 SHOW ENGINE INNODB STATUS 里的日志水位差。











