必须先终止trx_state = 'RUNNING'且trx_query IS NULL的事务,否则purge线程卡在最老ReadView导致调参无效;需用INNODB_TRX按trx_started筛选超600秒的空闲事务,关联PROCESSLIST确认后分类KILL,再加大innodb_purge_batch_size并执行ALTER UNDO TABLESPACE TRUNCATE释放磁盘空间。

必须先终止 trx_state = 'RUNNING' 且 trx_query IS NULL 的事务,否则 purge 线程永远卡在最老 read view 处,调任何参数都无效。
怎么快速定位真正卡住 purge 的长事务
别信 SHOW PROCESSLIST 的 Time 字段——它只统计当前语句执行时长,而真正钉死 purge 的是那些早已空闲、却没提交的连接。关键看 INNODB_TRX:
- 运行
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; - 重点筛选:
trx_state = 'RUNNING'且trx_query IS NULL(极大概率是应用漏了COMMIT或连接池未 close) -
trx_rows_modified > 10000且duration_sec > 300:大写事务已卡住,需立刻干预 - 用
trx_mysql_thread_id关联information_schema.PROCESSLIST,确认HOST、USER和COMMAND,排除监控/备份等合法长连接
kill 前必须分状态判断,否则可能更糟
盲目 KILL 不仅无效,还可能让 IO 更爆、purge 更慢,甚至引发雪崩:
-
trx_state = 'RUNNING'且trx_query IS NULL:可安全KILL,99% 是连接泄漏或 ORM 事务未关闭 -
trx_state = 'LOCK WAIT':先查INNODB_LOCK_WAITS找出blocking_trx_id,优先干掉上游持锁者 -
trx_state = 'ROLLING BACK':别动,此时KILL会让回滚从同步变异步,耗时翻倍、IO 更爆 - 若
SHOW ENGINE INNODB STATUS\G中History list length> 10000,批量KILL必须加SLEEP(0.2)间隔执行,避免 purge 线程瞬间过载
杀完后怎么让 purge 跟上并触发 undo 截断
杀掉源头不等于问题结束——History list length 不会立刻下降,得主动助推:
- 检查 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→ 等STATE变为INACTIVE→ALTER UNDO TABLESPACE undo_001 TRUNCATE
最常被忽略的一点:即使 purge 跟上了、History list length 已回落,undo 文件大小也不会自动缩小。InnoDB 不会把空页还给文件系统;想真正释放磁盘空间,必须走 TRUNCATE 流程,且前提是对应 undo 表空间已处于 INACTIVE 状态、无任何活跃事务依赖。


















