mysql 5.7 升级至 8.0 后 ibdata1 或 undo_001 暴涨,主因是默认启用独立 undo 表空间(如 undo_001)、innodb_undo_log_truncate=off、redo 日志文件重建变大、长事务阻塞 purge,以及 innodb_file_per_table=off 的旧表持续写入 ibdata1。

MySQL 升级后 ibdata1 或 undo_001 突然暴涨,先看 Undo Log 是否被自动迁移
MySQL 5.7 升级到 8.0 后,InnoDB 默认将 Undo Log 从系统表空间(ibdata1)剥离,改用独立的 undo 表空间文件(如 undo_001、undo_002)。若升级时未显式配置,MySQL 8.0+ 会自动创建两个 128MB 的 undo 文件,并持续写入——这正是磁盘占用翻倍的常见源头。
执行以下命令确认当前 Undo 配置:
SHOW VARIABLES LIKE 'innodb_undo%';
重点关注:
-
innodb_undo_tablespaces:应为 ≥2(默认值),表示独立 undo 文件数量 -
innodb_undo_log_truncate:必须为ON,否则 undo 空间永不回收 -
innodb_undo_log_encrypt:若开启且密钥轮转异常,也可能导致旧段无法清理
若 innodb_undo_log_truncate=OFF,立即执行:SET GLOBAL innodb_undo_log_truncate = ON;
innodb_log_file_size 被隐式放大,Redo Log 文件体积失控
MySQL 8.0 对 innodb_log_file_size 的默认值做了调整(部分发行版从 48MB 提高到 128MB 或 256MB),且升级过程不会自动缩容旧日志文件。你看到的 ib_logfile0 和 ib_logfile1 可能仍是旧尺寸,但新实例启动时若配置不匹配,会重建为更大尺寸——直接吃掉数 GB 磁盘。
检查实际 Redo 文件大小:
ls -lh /var/lib/mysql/ib_logfile*
再比对配置:
SHOW VARIABLES LIKE 'innodb_log_file_size';
若两者不一致(比如配置是 128M,但磁盘上文件是 256M),说明升级时 MySQL 重建了日志。安全缩容需分两步:
- 先设
innodb_log_file_size为期望值(如 128M),写入my.cnf并重启(MySQL 会拒绝启动并报错InnoDB: Error: log file ib_logfile0 is of different size) - 停库 → 手动删除
ib_logfile*→ 再启动,MySQL 自动重建指定大小的日志 - 注意:
innodb_fast_shutdown=0必须提前设置,确保 shutdown 是干净的,避免 recovery 失败
升级后 Undo 表空间未自动收缩,undo_001 持续增长但不释放
即使 innodb_undo_log_truncate=ON,Undo 表空间也不会“自动变小”,只会把可回收段标记为空闲、供后续事务复用。所以 du -sh undo_001 显示体积不变是正常现象——但如果你观察到它还在持续增大(比如每天 +500MB),说明有长事务或未提交事务在 hold undo 记录。
排查方法:
- 查活跃事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 300;(超 5 分钟的事务) - 查历史长事务残留:
SELECT * FROM performance_schema.events_transactions_history_long WHERE EVENT_NAME = 'transaction' ORDER BY TIMER_START DESC LIMIT 10; - 确认无业务影响后,可 kill 掉阻塞事务:
KILL <code>trx_mysql_thread_id;
注意:innodb_undo_tablespaces 设置后无法动态减少数量,想删掉一个 undo 文件只能导出数据、初始化新实例再导入。
别忽略 innodb_file_per_table=OFF 导致的隐性膨胀
老版本 MySQL 常关闭 innodb_file_per_table,所有表数据都挤进 ibdata1;升级后该参数默认为 ON,但已有表不会自动迁移。结果就是:新表走独立 .ibd,老表继续往 ibdata1 里写,而 ibdata1 一旦增长就永远不缩容。
判断是否中招:
SELECT table_schema, table_name, engine, row_format FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys') AND engine='InnoDB' AND row_format='Compact' LIMIT 5;
如果返回结果中 row_format 是 Compact 或 Redundant(而非 Dynamic 或 Compressed),大概率是老表未重建。此时最稳妥的做法是:
- 对关键大表执行
ALTER TABLE tbl_name ENGINE=InnoDB ROW_FORMAT=DYNAMIC; - 该操作会重建表并生成独立 .ibd,同时释放
ibdata1中对应空间(仅限该表) - 注意:需预留足够磁盘空间,且会锁表(除非用
ALGORITHM=INPLACE且满足条件)
真正难处理的是那些既大又不能停写的老表——它们占着 ibdata1 却无法收缩,只能靠业务低峰期分批迁移。











