必须先查INNODB_TRX定位最老活跃事务(trx_state='RUNNING'且trx_query IS NULL),再新增undo表空间、设旧表空间为inactive,待HISTORY_LIST_LENGTH下降后执行TRUNCATE;跳过任一环节或仅调参均会失败。

MySQL 8.0 中 undo 表空间膨胀不是“慢”,而是“悄无声息吃光磁盘+拖垮长事务性能”——必须主动查、不能等告警。
怎么看 undo 文件到底占了多少空间?
别只看 du -sh /var/lib/mysql/undo_*,那只是文件大小;得确认它是否真被 InnoDB 当前使用,以及是否处于可回收状态。
- 先查当前活跃的 undo 表空间数量和状态:
SELECT NAME, SPACE_TYPE, FILE_SIZE, INITIAL_SIZE, MAX_SIZE, AUTOEXTEND_SIZE FROM information_schema.INNODB_TABLESPACES WHERE SPACE_TYPE = 'Undo'; - 再看哪些被标记为 inactive:
SELECT TABLESPACE_NAME, STATE FROM information_schema.INNODB_TABLESPACES WHERE SPACE_TYPE = 'Undo' AND STATE = 'inactive'; - 如果
ibtmp1或ibdata1异常大,但你已启用独立 undo(innodb_undo_tablespaces > 2),说明 undo 可能还混在系统表空间里,需检查innodb_undo_logs和innodb_undo_directory配置是否生效
为什么 undo 没自动收缩?关键开关没开
MySQL 8.0 的 undo 自动截断(truncate)依赖两个硬性条件同时满足,缺一不可。很多人只设了其中一个,结果白配。
-
innodb_undo_log_truncate必须为ON(默认是 OFF) -
innodb_undo_tablespaces必须 ≥ 2(官方强制要求:至少 2 个才能轮换,否则无法标记 inactive) - 还要确保
innodb_max_undo_log_size设得合理(默认 1G),太小会导致频繁 truncate 增加开销,太大则延迟释放 - 执行
SET PERSIST innodb_undo_log_truncate = ON;后,必须等一次完整的 purge cycle(通常几分钟)才开始生效,不是立刻缩容
如何安全替换并释放一个 71G 的膨胀 undo?
不能直接 DROP TABLESPACE,也不能 rm 文件。正确路径是“新增→切换→标记→等待→清理”四步闭环。
- 添加新 undo 表空间:
CREATE UNDO TABLESPACE undo003 ADD DATAFILE 'undo003.ibu';(注意路径权限和磁盘剩余空间) - 将旧的(如
undo001)设为 inactive:ALTER UNDO TABLESPACE undo001 SET INACTIVE; - 确认它进入
inactive状态后,innodb_undo_log_truncate=ON会自动触发截断——但前提是:没有活跃事务在用它,且 purge 线程已完成对其上日志的清理 - 等
INFORMATION_SCHEMA.INNODB_TABLESPACES显示其FILE_SIZE显著下降(比如从 71G → 16M),再执行DROP UNDO TABLESPACE undo001;
容易被忽略的三个真实坑点
线上操作最怕“看着成功,实则埋雷”。这几个细节不盯紧,undo 就会反复膨胀。
- 升级后若沿用 MySQL 5.7 的配置,
innodb_undo_tablespaces很可能仍是 0 或 1 —— 这会导致SET INACTIVE报错ER_UNDO_TABLESPACE_IN_USE,必须先调大再操作 -
innodb_purge_rseg_truncate_frequency默认是 128,值越大 purge 越懒,inactive undo 的日志清理就越慢;高负载库建议调到 32 - 如果应用存在超长事务(>1 小时未提交),它会一直 hold 住某个 rollback segment,导致对应 undo 表空间永远无法标记 inactive —— 所以查
INFORMATION_SCHEMA.INNODB_TRX必须是第一步



















