innodb_undo_log_truncate=ON后文件大小不变是正常设计行为,仅逻辑截断(重置LSN、清空页数据、标记可复用),不调用ftruncate()缩小物理文件;真正释放磁盘空间必须执行“新增undo表空间→设旧表空间为INACTIVE→purge完成→DROP”完整流程。

innodb_undo_log_truncate=ON 开启后文件大小不变,不是配置失败,是 InnoDB 的设计行为:它只做逻辑截断(重置 LSN、清空页数据、标记空间可复用),不会调用 ftruncate() 缩小物理文件尺寸。真正释放磁盘空间必须走“新增 → 设 inactive → purge 完成 → DROP”闭环。
为什么 SET GLOBAL innodb_undo_log_truncate = ON 没效果
MySQL 会静默跳过截断逻辑,只要以下任一条件不满足:
-
innodb_undo_tablespaces值小于 2:默认为 2,但若被误设为 0 或 1,无法动态修改,只能重启或新建实例 -
innodb_max_undo_log_size≤ 0:默认 1024(MB),设为 0 直接禁用截断 - 所有 undo 表空间的
TRUNCATE_STATUS都不是truncate_ready:查SELECT NAME, TRUNCATE_STATUS FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE 'undo%'; - 存在长事务阻塞 purge:执行
SELECT trx_id, trx_started, trx_rows_modified FROM INFORMATION_SCHEMA.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5;,重点关注运行超 300 秒或修改行数 > 10000 的事务
如何确认 purge 是否就绪,才能安全 DROP UNDO TABLESPACE
别只看变量,要盯实时状态:
- 执行
SHOW ENGINE INNODB STATUS\G,搜索HISTORY LIST LENGTH—— 必须降到 500 以下才稳妥 - 查表空间状态:
SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE 'undo%';,目标表空间必须是INACTIVE,且最终变为empty - 再跑一次
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX,确保没有trx_state = 'RUNNING'且TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 600的事务
怎样安全新增 undo 表空间并轮转旧文件
这是释放磁盘空间唯一可行路径。系统保护的 innodb_undo_001 和 innodb_undo_002 不能直接 DROP,必须先加第三个:
- 创建新表空间:
CREATE UNDO TABLESPACE undo_003 ADD DATAFILE 'undo_003.ibu';(注意后缀必须是.ibu,名称不能以innodb_开头) - 等待 purge 推进:
SHOW ENGINE INNODB STATUS\G中HISTORY LIST LENGTH稳定在 500 以下后再操作 - 设旧表空间为 inactive:
ALTER UNDO TABLESPACE innodb_undo_001 SET INACTIVE;(若报错ERROR 3655,说明只剩两个 active 表空间,必须先加第三个) - 确认
STATE变为empty后,执行DROP UNDO TABLESPACE innodb_undo_001;
为什么 ALTER TABLESPACE undo_001 ENGINE=InnoDB 完全无效
这条语句对 undo 表空间既不报错也不起作用,属于社区误传的过时经验:
- InnoDB 明确不支持对 undo 类型表空间执行
ENGINE=InnoDB操作 - 执行后
INFORMATION_SCHEMA.INNODB_TABLESPACES.FILE_SIZE和ALLOCATED_SIZE完全不变 - 错误日志里查不到任何相关记录,官方文档从未将其列为 undo 管理手段
- 真正能回收磁盘空间的操作只有两个:
DROP UNDO TABLESPACE(需前置条件完备),或 MySQL 8.0.30+ 的OPTIMIZE TABLESPACE undo_001(仅优化内部碎片,不缩文件)
最常被忽略的一点:即使所有参数正确、状态就绪,innodb_undo_truncate_frequency(默认 128)也控制着截断尝试的频率——它不是实时触发,而是每完成约 128 次 purge 操作才检查一次是否该截断。这个值太小会加重 I/O,太大则延迟明显,调优前务必确认 purge 线程本身没被长事务卡死。


















