【说明】:
最近云服务器迁移,将原来的单服务器升级成主从模式,完成迁移后,发现系统盘I/O异常,执行【iostat -x 5】,磁盘%util基本上是100%,经查是由MySQL进程引起,经多方排查,最终锁定是由MySQL Redo Log 和 Undo Log导致的,将Redo Log 和 Undo Log迁移到另一块云盘后,顺利将I/O降至20%以下。现将过程记录如下。
【环境】:
MySQL 版本:8.0,采用主从复制模式
默认源目录:/var/lib/mysql
目标目录示例:/data/mysql-log
系统及版本:阿里云Ubuntu 22.04
1 Redo Log 和 Undo Log 简介
1.1 Redo Log(重做日志)
(1)作用:保证事务的持久性。当数据页修改后,先写入 Redo Log(顺序写),再异步刷入数据文件。崩溃恢复时,通过 Redo Log 重放已提交的事务修改,防止数据丢失。
(2)存储位置:MySQL 8.0 中,Redo Log 存储在 datadir/#innodb_redo/ 目录下(默认 /var/lib/mysql/#innodb_redo/),由多个 #ib_redo* 文件组成。
(3)I/O 特征:顺序写,高带宽消耗,对延迟敏感。
1.2 Undo Log(撤销日志)
(1)作用:保证事务的原子性(回滚)和 MVCC(多版本并发控制)。记录修改前的旧值,用于事务回滚或为一致性读提供历史版本。
(2)存储位置:独立表空间文件 undo_001, undo_002 等(MySQL 8.0 默认创建 2 个),位于 datadir 下(默认 /var/lib/mysql/undo_001, undo_002)。
(3)I/O 特征:随机读写,高并发写入时压力显著。
2 为什么要迁移 Redo/Undo Log
在高负载的生产环境中,Redo/Undo Log 会产生大量磁盘 I/O。默认情况下,它们与数据文件、系统表空间等共同存放在 datadir(通常为系统盘),导致以下问题:
- 磁盘 I/O 争用:多种 I/O 类型(顺序写、随机写、读写)混合在同一磁盘,相互干扰,性能下降。
- 系统盘压力过大:系统盘(如 vda)往往性能有限(高效云盘 IOPS 约 3000),容易达到瓶颈,影响整体稳定性。
- 无法灵活扩展:数据文件和日志混在一起,无法单独升级日志盘的性能。
- 迁移目标:将 Redo/Undo Log 分离到独立的、高性能的磁盘(如 ESSD),实现 I/O 隔离,降低系统盘压力,提升数据库吞吐量。
3 迁移前准备
3.1 硬件规划
准备一块或多块独立磁盘(建议 ESSD PL0 或更高),挂载到指定目录,例如 /data。
在 /data 下创建迁移目标目录:/data/mysql-log。
确保磁盘有足够空间(Redo Log 按 innodb_redo_log_capacity 设置,Undo Log 按 innodb_max_undo_log_size 设置,建议额外预留 20%)。
3.2 目录权限
bash
mkdir -p /data/mysql-log/mysql_redo /data/mysql-log/mysql_undo
chown mysql:mysql /data/mysql-log/mysql_redo /data/mysql-log/mysql_undo
chmod 750 /data/mysql-log/mysql_redo /data/mysql-log/mysql_undo
3.3 备份
备份 MySQL 配置文件(/etc/mysql/mysql.conf.d/mysqld.cnf)。
建议对 datadir 进行全量备份(可使用 rsync 或 mysqldump)。
4 迁移步骤
4.1 停止 MySQL 服务
bash
systemctl stop mysql
4.2 迁移 Undo Log(通过配置文件)
bash
# 移动现有 Undo 文件(从默认目录到新目录)
mv /var/lib/mysql/undo_* /data/mysql-log/mysql_undo/
编辑 /etc/mysql/mysql.conf.d/mysqld.cnf,在 [mysqld] 段末尾添加如下内容:
bash
innodb_undo_directory = /data/mysql-log/mysql_undo
innodb_undo_log_truncate = ON
innodb_max_undo_log_size = 1G # 可根据需要调整
4.3 迁移 Redo Log
由于 MySQL 8.0 强制 Redo Log 位于 datadir/#innodb_redo,无法通过参数修改路径,因此这里采用符号链接。
bash
# 移动原 Redo 目录
mv /var/lib/mysql/#innodb_redo /data/mysql-log/mysql_redo/
# 创建符号链接
ln -s /data/mysql-log/mysql_redo/#innodb_redo /var/lib/mysql/#innodb_redo
chown -h mysql:mysql /var/lib/mysql/#innodb_redo
4.4 修正目录权限
bash
chown -R mysql:mysql /data/mysql-log
chmod 750 /data/mysql-log/mysql_redo /data/mysql-log/mysql_redo/#innodb_redo
chmod 750 /data/mysql-log/mysql_undo
# 确保父目录有执行权限(允许 mysql 用户穿透)
sudo chmod 755 /data /data/mysql-log
4.5 处理安全模块
Ubuntu 等系统默认启用 AppArmor,会阻止 MySQL 访问新路径。解决方案:
(1)临时禁用 MySQL 的 AppArmor 限制(快速测试)
bash
aa-disable /usr/sbin/mysqld
(2)永久添加规则(推荐)
编辑 /etc/apparmor.d/usr.sbin.mysqld,在末尾(但要"}"内部)添加:
bash
/data/mysql-log/ r,
/data/mysql-log/** rwk,
重新加载AppArmor:
bash
apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
systemctl restart apparmor
4.6 启动 MySQL 并验证
bash
systemctl start mysql
# 检查无权限错误
tail -f /var/log/mysql/error.log
5 常见故障排除
5.1 启动失败,错误日志显示 error 13 (Permission denied)
**原因:**MySQL 用户无权访问目标路径,或中间目录缺少执行权限,或 AppArmor/SELinux 阻止。
解决方案: 检查各级目录权限:ls -ld /data /data/mysql-log,确保 mysql 用户有 +x 权限。
临时禁用 AppArmor:sudo aa-disable /usr/sbin/mysqld。
5.2 启动失败,错误日志显示 Invalid Filename
**原因:**Undo 文件未成功移动到新目录,但 innodb_undo_directory 已指向新路径。
**解决方案:**将 Undo 文件移动到正确位置,或暂时注释 innodb_undo_directory 让 MySQL 使用默认路径。
5.3 迁移后磁盘 I/O 利用率依然很高
**原因:**新磁盘性能不足(例如仍为高效云盘),或 MySQL 参数未优化。
解决方案:
升级磁盘到 ESSD PL2 或更高。
调整 innodb_flush_log_at_trx_commit = 2 和 sync_binlog = 1000(主库)。具体参见6.1。
检查是否还有其他组件(如 Elasticsearch)占用同一磁盘。
5.4 符号链接在 Ubuntu 下被 AppArmor 拒绝
**原因:**AppArmor 会追踪符号链接的最终目标,即使链接本身在允许目录内,目标路径也必须在策略中允许。
**解决方案:**检查、重载并重启AppArmor 。
6 迁移后的优化建议
6.1 调整 MySQL 参数降低 I/O 压力
迁移成功后,系统盘的I/O确实显著下降了,但数据盘vdb的I/O却高达60%左右,可编辑MySQL配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf,在末尾添加如下内容(注意:主/从配置略有不同)
sql
# 主库(可容忍最多丢失 1 秒数据 + 最多 1000 个事务)
innodb_flush_log_at_trx_commit = 2
sync_binlog = 1000
# 从库(完全不产生 binlog 时可设 sync_binlog=0)
innodb_flush_log_at_trx_commit = 2
sync_binlog = 0
重启MySQL服务。经此优化后,I/O可大幅下降,甚至降至20%以下。
6.2 增大 Redo Log 容量
sql
SET PERSIST innodb_redo_log_capacity = 16G;
6.3 定期监控磁盘利用率
sql
iostat -x 5 | grep -E "vdb|vdc|vdd"
7 总结
通过将 Redo/Undo Log 从默认目录 /var/lib/mysql 迁移到独立的高性能磁盘 /data/mysql-log,并配合合理的 MySQL 参数优化,可以显著降低系统盘 I/O 压力,提升数据库整体吞吐量。迁移过程中遇到权限或安全模块问题时,优先使用绑定挂载方案,或临时禁用 AppArmor 进行验证。迁移后应持续监控磁盘性能,必要时升级云盘类型。