Undo Log 是 InnoDB 实现事务回滚和MVCC(多版本并发控制)的核心机制,记录事务修改前的旧版本数据。
一、核心作用与生命周期(Mermaid 图)
二、MySQL 8.0 的 Undo 革命
8.0 之前的痛点(5.7 及以下)
Undo 默认存储在
ibdata1共享表空间无法收缩 :长事务导致
ibdata1持续膨胀,只能重建实例性能瓶颈 :Undo 与数据混用表空间,I/O 竞争
8.0+ 的革新
Undo 独立表空间:自动创建
undo_001~undo_010(10 个文件)在线 Truncate :Purge 后自动释放空间,无需停机
可配置数量 :
innodb_undo_tablespaces(静态参数,需重启)可配置路径 :
innodb_undo_directory(可自定义到高速磁盘)
# MySQL 8.0 数据目录
/var/lib/mysql/
├── undo_001 # Undo 独立文件
├── undo_002
├── ...
├── ibdata1 # 不再存 Undo,只存数据字典
└── mydb/
└── users.ibd三、关键配置参数
1. innodb_undo_log_truncate(动态)
[mysqld]
innodb_undo_log_truncate = ON # 默认 ON,自动收缩 Undo 文件作用 :当 Undo 文件超过
innodb_max_undo_log_size时,Purge 后自动 Truncate风险 :关闭后 Undo 文件只增不减
2. innodb_max_undo_log_size(动态)
[mysqld]
innodb_max_undo_log_size = 1G # 默认 1GB作用:Undo 文件达到此大小后,标记为可 Truncate
调大场景:长事务频繁,避免频繁 Truncate 开销
3. innodb_undo_tablespaces(静态,重启生效)
[mysqld]
innodb_undo_tablespaces = 3 # 默认 2(8.0),推荐 3-5作用:Undo 文件数量,分散 I/O 压力
规则:必须是 2~127 的整数,且 ≤
innodb_open_files8.0.14+ 默认 2 ,通常无需调整
4. innodb_undo_directory(静态)
[mysqld]
innodb_undo_directory = /ssd/undo # 默认空(数据目录)作用:将 Undo 文件定向到独立高速磁盘(如 NVMe)
场景:高并发写入,Undo I/O 与数据 I/O 分离
5. innodb_purge_threads(动态)
[mysqld]
innodb_purge_threads = 4 # 默认 4作用:Purge 线程数量,清理无用 Undo
调大场景:大量 UPDATE/DELETE 后,Undo 堆积不释放
四、关键监控指标
1. Undo 空间占用
-- 查看 Undo 表空间大小
SELECT
NAME,
FILE_NAME,
ROUND(SIZE / 1024 / 1024, 2) AS size_mb,
ROUND(MAX_SIZE / 1024 / 1024, 2) AS max_size_mb,
STATE
FROM information_schema.INNODB_TABLESPACES
WHERE NAME LIKE 'innodb_undo%';2. 长事务与 Undo 堆积
-- 查询持有 Undo 的活跃事务
SELECT
trx_id, trx_state, trx_started,
trx_mysql_thread_id, trx_rows_modified,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_sec
FROM information_schema.INNODB_TRX
WHERE trx_state = 'RUNNING'
AND TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60; -- 超过 60 秒3. Purge 延迟
-- 查看 Purge 进度
SHOW ENGINE INNODB STATUS\G
-- 输出中:
-- ------------
-- TRANSACTIONS
-- ------------
-- Purge done for trx's n:o < 12345 undo n:o < 67894. Undo 历史版本链长度
-- 查看最长版本链(MVCC 影响)
SELECT
TABLE_NAME,
MAX_UNDO_LOG_SIZE,
MIN_UNDO_LOG_SIZE
FROM information_schema.INNODB_TABLESPACES
WHERE NAME LIKE 'innodb_undo%';五、最佳实践
1. 默认配置(90% 场景)
[mysqld]
# MySQL 8.0+ 默认已最优
innodb_undo_log_truncate = ON
innodb_max_undo_log_size = 1G
innodb_undo_tablespaces = 2 # 无需修改
innodb_purge_threads = 4 # 默认足够无需手动调优,Undo 自动管理。
2. 高并发写入场景
[mysqld]
innodb_undo_tablespaces = 5 # 增加到 5 个文件
innodb_max_undo_log_size = 2G # 避免频繁 Truncate
innodb_purge_threads = 8 # 增加 Purge 线程
innodb_undo_directory = /nvme/undo # 分离到 NVMe3. 长事务管控(避免 Undo 堆积)
[mysqld]
# 限制事务时长(秒)
innodb_lock_wait_timeout = 50
wait_timeout = 600
# 监控长事务告警:duration > 300 秒六、常见问题与排查
问题 1:Undo 文件持续增大不释放
-- 排查步骤
1. SHOW ENGINE INNODB STATUS\G 查看活跃事务
2. SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 10;
3. 找到 trx_mysql_thread_id,KILL 长事务
4. 等待 Purge 自动清理(或手动触发:无直接命令,靠 InnoDB 自动)问题 2:磁盘空间告警,Undo 占满
# 紧急处理(业务低峰期)
SET GLOBAL innodb_undo_log_truncate = ON; -- 确保开启
SET GLOBAL innodb_max_undo_log_size = 512M; -- 降低阈值
# 查看清理进度
ls -lh /var/lib/mysql/undo_*
-- 文件大小会逐渐缩小问题 3:Purge 太慢,Undo 堆积
-- 查看 Purge 线程状态
SHOW PROCESSLIST;
-- 找到 'purge' 线程,观察 Time 列
-- 调大线程数
SET GLOBAL innodb_purge_threads = 8;七、长事务导致 Undo 暴增的实战案例
场景 :凌晨批处理 UPDATE 1000 万行,未提交,Undo 占用 200GB
-- 1. 查看 Undo 大小(此时 undo_001 已 150GB)
SELECT NAME, ROUND(SIZE/1024/1024/1024, 2) AS size_gb
FROM information_schema.INNODB_TABLESPACES
WHERE NAME LIKE 'innodb_undo%';
-- 2. 查看长事务
SELECT trx_id, trx_mysql_thread_id, trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_sec
FROM information_schema.INNODB_TRX
ORDER BY duration_sec DESC;
-- 结果:trx_id 12345,duration_sec 7200(2 小时)解决 :
-- 杀会话(需业务确认可回滚)
KILL 12345; -- trx_mysql_thread_id
-- 等待 Purge 清理(约 10-30 分钟)
-- 监控:
SHOW STATUS LIKE 'Innodb_history_list_length'; -- 持续下降预防 :
[mysqld]
# 限制单个事务修改行数
innodb_max_undo_log_size = 1G # 达到后尽早触发 Truncate
# 应用层拆分大事务为 10 万行一批八、Undo 与 Redo 的关系(PlantUML)
九、总结与配置速查
核心记忆点
Undo 是 MVCC 和回滚 ,不是 Binlog 或 Redo
MySQL 8.0+ Undo 自动管理 ,默认无需调优
长事务是唯一敌人 ,需监控
INNODB_TRX.duration_secUndo 文件过大 → 杀长事务 → 等待 Purge
innodb_undo_log_truncate = ON必须开启
生产环境巡检 SQL
-- 一键巡检脚本
SELECT
'Undo 文件大小' AS check_item,
CONCAT(ROUND(SUM(SIZE)/1024/1024/1024, 2), ' GB') AS value
FROM information_schema.INNODB_TABLESPACES
WHERE NAME LIKE 'innodb_undo%'
UNION ALL
SELECT
'最长事务时长(秒)',
MAX(TIMESTAMPDIFF(SECOND, trx_started, NOW()))
FROM information_schema.INNODB_TRX
UNION ALL
SELECT
'Purge 线程数',
@@innodb_purge_threads;健康标准 :
Undo 总大小 < 10GB(或 <
innodb_max_undo_log_size × 表空间数)最长事务 < 300 秒
Purge 线程 = 4(默认)
一句话 :Undo Log 的问题,90% 是 长事务,10% 是配置。
评论