安全清理undo文件需三步:先查并终止长事务(information_schema.innodb_trx)、再调优purge参数(innodb_purge_rseg_truncate_frequency等)、最后手动截断空闲undo表空间(alter undo tablespace ... truncate)。

直接删 undo_001、undo_002 文件会触发 InnoDB 启动失败或数据损坏,这不是日志缓存,而是 MVCC 历史版本的持久化载体。安全清理的核心是:先解除阻塞源,再推动 Purge,最后触发截断 —— 三步缺一不可。
查长事务:INFORMATION_SCHEMA.INNODB_TRX 是第一现场
所有 Undo 堆积的起点,几乎都是某个“活着但不动”的事务。它不提交、不回滚、甚至可能只是个空闲连接挂着 read view。
-
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5;—— 看TRX_STARTED时间戳,超过 300 秒就值得怀疑 - 重点盯
TRX_STATE = 'RUNNING'且TRX_ROWS_LOCKED > 0的行,这类事务大概率卡在应用层未COMMIT - 别漏掉
TRX_STATE = 'LOCK WAIT',它可能被上游长事务锁住,形成链式阻塞 - 执行
XA RECOVER;,若有输出,必须手动XA COMMIT或XA ROLLBACK,否则对应 undo 永远不可截断
验配置有效性:innodb_undo_tablespaces 必须 ≥2 且已重启
很多人开了 innodb_undo_log_truncate = ON 却没效果,根本原因是 innodb_undo_tablespaces 还是默认的 0 —— 这意味着所有 undo 全挤在 ibdata1 里,根本没法轮换、没法截断。
-
SHOW VARIABLES LIKE 'innodb_undo%';—— 若innodb_undo_tablespaces返回 0 或 1,说明配置无效,innodb_undo_log_truncate开了也白搭 -
innodb_undo_tablespaces只能在初始化实例时设置,运行中修改不生效,必须停库改配置 +service mysqld restart(reload不行) - 确认后查
SHOW STATUS LIKE 'Innodb_undo_log_truncated';—— 返回值长期为 0,说明 Purge 没跑起来,或仍有长事务残留
推 Purge 线程:调小 innodb_purge_rseg_truncate_frequency
Purge 默认每 128 次内部调用才尝试释放一次回滚段,响应太慢。Undo 堆积严重时,这个频率得临时拉高,否则 kill 完长事务,空间也不会马上收缩。
-
SET GLOBAL innodb_purge_rseg_truncate_frequency = 32;—— 值越小,释放越频繁;但别设成 1,会引发 CPU 抖动 - 配合加大单次清理力度:
SET GLOBAL innodb_purge_batch_size = 10000; - 执行
FLUSH LOGS;强制刷写日志,有助于释放 purge 线程持有的锁资源 - 观察
SHOW ENGINE INNODB STATUS\G中的History list length是否开始下降(理想是每分钟降几百以上)
强制截断 undo 表空间:ALTER UNDO TABLESPACE ... TRUNCATE
只有当某个 undo 表空间完全空闲(无活跃回滚段)时,才能被截断。自动截断有延迟,而业务压测或磁盘告警时,需要人工介入。
-
ALTER UNDO TABLESPACE undo_001 TRUNCATE;—— 注意表空间名要和SHOW VARIABLES LIKE 'innodb_undo_tablespaces';对应,不是所有都叫undo_001 - 一次只截断一个,确保至少有一个 undo 表空间保持可用,避免事务分配失败
- 截断后文件大小重置为 10MB(由
innodb_max_undo_log_size控制,默认 128MB,但截断目标是初始大小) - 若报错
ERROR 3104 (HY000): Cannot truncate undo tablespace 'undo_001' because it is not empty,说明还有活跃 segment,回去再查INNODB_TRX和XA RECOVER
最易被忽略的一点:即使所有配置正确、长事务清空、Purge 频率调高,undo 文件也不会“立刻变小”。它需要 Purge 线程先完成回滚段释放,再触发截断逻辑,整个过程可能持续几分钟到几十分钟,取决于 history list 长度和 I/O 负载。盯着 Innodb_undo_log_truncated 状态值上升,比看 du -sh 更可靠。











