必须先终止trx_state='RUNNING'且trx_query IS NULL的最老事务,否则所有调参和清理均无效;需用INNODB_TRX按trx_started排序定位,关联PROCESSLIST确认异常连接,再分状态KILL,最后加大innodb_purge_batch_size并启用undo截断。

立刻终止 trx_state = 'RUNNING' 且 trx_query IS NULL 的最老事务,否则所有后续操作都无效。
怎么快速定位真正卡住 purge 的长事务
别信 SHOW PROCESSLIST 的 Time 字段——它只算当前语句执行时长。真正钉死 undo 的,是那些已空闲但没提交的连接。
- 运行这条语句查最老的活跃事务:
SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS duration_sec, trx_state, trx_rows_modified, trx_mysql_thread_id FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 600 ORDER BY trx_started LIMIT 5; - 重点关注:
trx_state = 'RUNNING'且trx_query IS NULL(空闲未提交)、duration_sec > 600、trx_rows_modified > 10000 - 用
trx_mysql_thread_id关联information_schema.PROCESSLIST,确认HOST、USER、COMMAND和INFO,判断是否是 Python/Java 应用崩溃后连接未关闭
kill 前必须分状态处理,否则可能更糟
盲目 KILL 会加剧 IO 压力、拖垮 purge 线程,甚至让实例雪崩。
-
trx_state = 'RUNNING'且trx_query IS NULL:可安全KILL,99% 是 ORM 事务泄漏或连接池未 close -
trx_state = 'LOCK WAIT':先查INNODB_LOCK_WAITS找出blocking_trx_id,优先干掉上游事务 -
trx_state = 'ROLLING BACK':别动,此时KILL会让回滚变异步,耗时翻倍、IO 更爆 - 若
trx_rows_modified > 100000,回滚可能持续数分钟,KILL前务必评估业务影响
杀完之后怎么让 undo 空间真正释放出来
杀掉源头后,History list length 不会立刻下降,必须手动助推 purge 并安全缩容。
- 检查 purge 进度:
SHOW ENGINE INNODB STATUS\G,关注 “PURGE DONE for trx's n:o - 临时加大清理能力:
SET GLOBAL innodb_purge_batch_size = 10000(默认 300) - 确认已启用独立 undo 表空间:
SHOW VARIABLES LIKE 'innodb_undo_tablespaces',值必须 ≥ 4(推荐) - 开启自动截断:
SET GLOBAL innodb_undo_log_truncate = ON,再执行:ALTER UNDO TABLESPACE undo_001 SET INACTIVE→ 等History list length显著回落 →ALTER UNDO TABLESPACE undo_001 TRUNCATE
最容易被忽略的是:MySQL 8.0 要求至少保留 2 个 active 的 undo 表空间才能 truncate,而默认只有 2 个,所以必须先 CREATE UNDO TABLESPACE 新增第 3 个(路径必须以 .ibu 结尾),再设旧的为 inactive;否则 ALTER UNDO TABLESPACE ... SET INACTIVE 会直接报错 ERROR 3655。


















