暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL_数据目录迁移实战_Rsync两阶段换盘

原创 lucky、糯米汤圆 3天前
24

适用版本:MySQL 5.7 / 8.0 / 8.4
迁移方法:在线 Rsync 全量 + 停机 Rsync 增量 + 换盘位(一次停机完成)
核心场景:同一台服务器,将 MySQL 数据目录从旧硬盘(/data)迁移到新硬盘(/data1),停机阶段直接完成盘符互换,my.cnf 无需修改
预计停机时间:5–15 分钟(取决于增量数据量)


一、迁移方案概述

1.1 整体流程

阶段一:在线全量 Rsync(业务不停)
     /data/mysql ──rsync──> /data1/mysql
     │
     │  (预同步可重复多次,逐步缩小增量)
     │
阶段二:停机 + 增量同步 + 换盘位(唯一一次停机,5-15 分钟)
     ① 停库
     ② 最终 rsync(同步剩余增量)
     ③ 卸载 /data1(新盘)
     ④ 卸载 /data(旧盘,数据未被删除,物理还在)
     ⑤ 改 fstab:新盘挂载点 /data1 → /data
     ⑥ mount -a(新盘挂载为 /data)
     ⑦ 启动 MySQL(my.cnf 始终指向 /data/mysql,无需修改)
     ⑧ 验证

1.2 环境信息表

项目 当前值 迁移后值
数据目录 /data/mysql /data/mysql(不变,只是底层换到了新盘)
旧盘挂载点 /data 迁移完成后卸载,物理保留 3 天观察期
新盘挂载点 /data1(临时) /data
配置文件 /etc/my.cnf 全程不动,datadir=/data/mysql 始终有效

版本差异说明:

  • MySQL 5.7:数据目录下无 mysql.ibd(系统表在 .frm + MyISAM 文件中),无 #innodb_temp/ 目录
  • MySQL 8.0+:新增 mysql.ibd(DD 数据字典)、#innodb_temp/(临时表空间)、undo_001/002 等文件,rsync 排除规则需对应调整
  • MySQL 8.4:SHOW SLAVE STATUS / CHANGE MASTER TO 已废弃,统一改为 SHOW REPLICA STATUS / CHANGE REPLICATION SOURCE TO

二、前置准备

2.1 环境检查

# 确认当前数据目录位置和大小 df -h /data du -sh /data/mysql # 确认 MySQL 版本和运行状态 mysql -V systemctl status mysql # 确认配置文件路径 ps -ef | grep mysqld | grep 'my.cnf' # 确认现有卷组名(LVM 环境),用于 2.2 节 vgextend sudo vgs
-- 登录 MySQL,记录关键参数和二进制日志位置 SELECT @@datadir; SELECT @@innodb_log_file_size; SELECT @@innodb_log_files_in_group; SHOW VARIABLES LIKE 'log_bin%'; SHOW VARIABLES LIKE 'relay_log%'; -- MySQL 5.7 / 8.0(8.0.22 以下) SHOW MASTER STATUS; -- 记录 File 和 Position -- MySQL 8.0.22+ SHOW BINARY LOG STATUS;

2.2 新硬盘准备(LVM + XFS)

# 查看新硬盘设备名(假设新盘为 /dev/vdb) lsblk # ===== Step 1:LVM 初始化与逻辑卷创建 ===== pvcreate /dev/vdb # 将新磁盘初始化为物理卷(PV) vgextend mysql /dev/vdb # 将 PV 加入已有卷组 mysql(确认 VG 名:vgs) lvcreate -l 100%free -n data mysql # 在 VG mysql 中创建逻辑卷 data,使用全部剩余空间 # ===== Step 2:创建 XFS 文件系统 ===== mkfs.xfs /dev/mapper/mysql-data # ===== Step 3:挂载并写入 fstab ===== mkdir -p /data1 # 记录 UUID,用于写入 fstab(比设备路径更稳定) blkid /dev/mapper/mysql-data # 写入 fstab(临时挂载为 /data1,停机阶段改为 /data,避免遗忘) echo "/dev/mapper/mysql-data /data1 xfs defaults 0 0 # TODO: 停机后改为 /data" >> /etc/fstab # 根据 fstab 挂载 mount -a # ===== Step 4:验证挂载并准备数据目录 ===== df -h /data1 mkdir -p /data1/mysql chown mysql:mysql /data1/mysql chmod 750 /data1/mysql

说明:

  • VG 名建议用 vgs 确认,本文统一用 mysql(与旧盘 VG 保持一致)
  • LVM 逻辑卷路径格式为 /dev/mapper/<VG名>-<LV名>,本例为 /dev/mapper/mysql-data
  • XFS 不支持 fsck 强制修复,但具备完整日志,生产环境适合大数据目录

2.3 备份(必须在操作前完成)

# 方式一:mysqldump 逻辑备份(适合 100GB 以内) mysqldump --all-databases --single-transaction --master-data=2 \ > /tmp/mysql_full_backup_$(date +%Y%m%d).sql # 方式二:XtraBackup 物理备份(推荐大库,需提前安装 percona-xtrabackup) xtrabackup --backup --datadir=/data/mysql --target-dir=/tmp/mysql_backup_$(date +%Y%m%d) # 验证备份文件存在 ls -lh /tmp/mysql_full_backup_*.sql # 或 ls -lh /tmp/mysql_backup_*/ # 备份配置文件 cp /etc/my.cnf /etc/my.cnf.bak_$(date +%Y%m%d)

2.3.1 LVM 快照备份(可选,更安全的回退兜底)

如果旧盘使用了 LVM 且 VG 中有剩余空间,可在停机前给旧盘打快照,作为物理级别的秒级回退点:

# 创建快照(-L 指定快照大小,按实际写入量估算,建议 10-20G) lvcreate -L 10G -s -n mysql_snap /dev/mapper/mysql-olddata # 如果回退时需要从快照恢复旧盘数据 # lvconvert --merge /dev/mapper/mysql_snap # 合并完成后重启 MySQL # 快照不再需要时删除 # lvremove /dev/mapper/mysql_snap

三、第一阶段:在线全量同步(业务不停)

说明: 此阶段 MySQL 正常运行,业务不受影响。数据量越大,耗时越长(参考:200GB 约 2–4 小时),建议在业务低峰期启动。

3.1 排除文件说明

排除项 原因
ib_logfile* InnoDB Redo Log,启动时自动重建(MySQL 8.0.30+ 已改为 #ib_redo*,规则仍可用)
*.log 错误日志/慢查询日志/通用日志,重新生成即可
mysql.sock 运行时 Socket 文件,停库后自动消失
mysql.pid 运行时 PID 文件,停库后自动消失
auto.cnf 存储 server_uuid主库原地迁移必须保留,排除会导致 UUID 变化,所有从库主从断开;仅搭建新从库时才排除
#innodb_temp/* MySQL 8.0 临时表空间(MySQL 5.7 无此目录,排除规则无害)

3.2 执行首次全量同步

⚠️ 尾部斜杠不能省: /data/mysql/ 表示同步目录内容到目标目录内;去掉斜杠写成 /data/mysql 会在目标端多套一层子目录(变成 /data1/mysql/mysql/),后续所有步骤路径全部错乱。

# 用 screen 避免终端断开(无 screen 的系统改用 nohup) screen -S mysql_rsync rsync -av --progress --delete \ --exclude='ib_logfile*' \ --exclude='#ib_redo*' \ --exclude='*.log' \ --exclude='mysql.sock' \ --exclude='mysql.pid' \ --exclude='#innodb_temp/' \ /data/mysql/ /data1/mysql/ # 脱离 screen:Ctrl+A D;重连:screen -r mysql_rsync # 另一个终端监控进度 watch -n 60 "du -sh /data1/mysql" ps -ef | grep rsync

大文件加速: 有几十 GB 的 .ibd 文件时,rsync 默认 delta 校验很慢。加 -W 全文件复制,反正停库后数据不会再变:

rsync -avW --progress --delete --exclude=... /data/mysql/ /data1/mysql/

提示: 首次全量完成后,可在停机前多次重复执行相同命令,每次只同步增量,缩短最终停机时间。


四、第二阶段:停机 + 增量同步 + 换盘位(唯一停机窗口)

⚠️ 停机窗口开始,预计 5–15 分钟。my.cnf 全程不动。

4.1 停库前可选优化(降低崩溃恢复时间)

-- 登录 MySQL 执行,关闭所有打开的表并刷新表缓存,减少停库后的崩溃恢复时间 FLUSH TABLES;

4.2 停库

systemctl stop mysql sleep 5 ps -ef | grep mysqld | grep -v grep # 确认进程已完全退出,应无输出

4.3 执行最终增量同步

rsync -av --progress --delete \ --exclude='ib_logfile*' \ --exclude='#ib_redo*' \ --exclude='*.log' \ --exclude='mysql.sock' \ --exclude='mysql.pid' \ --exclude='#innodb_temp/' \ /data/mysql/ /data1/mysql/ # 文件数量对比 echo "旧目录: $(find /data/mysql -type f | wc -l)" echo "新目录: $(find /data1/mysql -type f | wc -l)"

4.4 校验新目录——静态检查

# 关键文件存在性检查 ls -la /data1/mysql/ | grep ibdata1 ls -la /data1/mysql/ | grep mysql.ibd # MySQL 8.0+ DD 字典,缺则无法启动 ls -la /data1/mysql/ | grep performance_schema # 权限修正 chown -R mysql:mysql /data1/mysql chmod 750 /data1/mysql

4.5 卸载旧盘、换盘位

核心思路: 旧盘数据在物理上未被删除,只是解除挂载。此步做完,/data 就指向新盘了。

# 确认无进程占用 lsof /data1; lsof /data # 执行前确认 /data 是独立挂载点(必须!) mountpoint /data # 返回 "/data is a mountpoint" → 继续 # 返回 "/data is not a mountpoint" → 说明旧数据在系统盘上(非独立分区), # 跳过 umount /data,直接 umount /data1 后 mount -a 即可; # 系统盘上的 /data 原内容会被新挂载遮盖,数据仍在但路径不可见 # 卸载新盘 umount /data1 # 卸载旧盘(仅在 /data 是独立挂载点时执行) umount /data

4.6 修改 fstab,新盘永久挂载为 /data

vi /etc/fstab

找到新盘那一行,挂载点从 /data1 改为 /data

# 改前
/dev/mapper/mysql-data   /data1   xfs   defaults   0 0

# 改后
/dev/mapper/mysql-data   /data    xfs   defaults   0 0

注释掉旧盘的挂载行:

# /dev/mapper/mysql-olddata   /data   xfs   defaults   0 0
# 挂载:此时 /data = 新盘,/data/mysql = 刚才 rsync 过来的数据 mkdir -p /data mount -a # 验证 df -h /data ls /data/mysql/ | head -10

4.7 启动 MySQL 并验证

my.cnf 全程没动,datadir=/data/mysql 依然有效。但底层 /data 已指向新盘,MySQL 读到的就是刚才 rsync 过来的数据。

systemctl start mysql systemctl status mysql # 启动失败时看错误日志 tail -100 /data/mysql/*.err

MySQL 8.0.30+ 启动失败补充排查: 如果错误日志提示 redo log 相关错误,可能是 #innodb_redo/ 空目录残留干扰了重建逻辑。手动清空后重试:

rm -rf /data/mysql/#innodb_redo/ systemctl start mysql # MySQL 会根据 innodb_redo_log_capacity 自动重建

五、启动后验证

5.1 数据库层验证

-- 确认 datadir 路径(应为 /data/mysql/,与迁移前一致) SELECT @@datadir; -- 确认数据库列表完整 SHOW DATABASES; -- 检查核心表数据 SELECT COUNT(*) FROM mysql.user; SELECT COUNT(*) FROM information_schema.tables; -- 检查 InnoDB 状态(无报错为正常) SHOW ENGINE INNODB STATUS\G -- 确认 Redo Log 已自动重建 SHOW VARIABLES LIKE 'innodb_log_file%'; ls /data/mysql/ib_logfile* -- MySQL 8.0.30+:ls /data/mysql/#innodb_redo/

5.2 主从复制验证(如果有从库)

-- MySQL 5.7 / 8.0(8.0.22 以下) SHOW SLAVE STATUS\G -- MySQL 8.0.22+ SHOW REPLICA STATUS\G -- 重点检查: -- Slave_IO_Running: Yes -- Slave_SQL_Running: Yes -- Seconds_Behind_Master: 0(或接近 0)

版本差异说明:

  • MySQL 8.0.22+:SHOW SLAVE STATUS 已废弃,改用 SHOW REPLICA STATUS
  • MySQL 8.0.23+:CHANGE MASTER TO 已废弃,改用 CHANGE REPLICATION SOURCE TO
  • MySQL 8.4:上述废弃命令已移除,仅支持新语法

5.3 应用层验证

# 本地连接测试 mysql -h 127.0.0.1 -uroot -p -e "SELECT 1" # 简单读写测试 mysql -uroot -p -e "CREATE DATABASE test_migrate; DROP DATABASE test_migrate;" # 确认应用业务连接正常(建议由业务方进行功能验证)

六、收尾与清理

6.1 更新关联配置与旧盘处置

# 备份脚本路径(如 crontab 中的 mysqldump 路径) crontab -l | grep mysql # Logrotate / 监控系统路径 grep -r '/data' /etc/logrotate.d/ # Zabbix / Prometheus / Grafana 等需手动更新 # 重新加载 systemd systemctl daemon-reload

旧盘处置: 旧盘数据已被卸载但物理上仍可恢复,保留至少 3 天观察期,期间不要格式化。3 天无异常后可安全拔除或重新利用。

6.2 一键检查脚本(迁移后用)

#!/bin/bash # mysql_migrate_verify.sh # 使用说明:先设置 MYSQL_PWD 环境变量或配置 ~/.my.cnf 免密 # export MYSQL_PWD=your_password # 或者:在 ~/.my.cnf 中配置 [client] user=root password=xxx echo "=== MySQL 迁移验证 ===" echo "1. MySQL 运行状态: $(systemctl is-active mysql)" echo "2. 数据目录: $(mysql -e 'SELECT @@datadir;' 2>/dev/null)" echo "3. 挂载点信息:" df -h /data echo "4. 数据目录权限:" ls -la /data/mysql/ | head -5 echo "5. 关键文件检查:" [ -f /data/mysql/ibdata1 ] && echo " ibdata1: ✓" || echo " ibdata1: ✗ 缺失!" [ -f /data/mysql/mysql.ibd ] && echo " mysql.ibd: ✓" || echo " mysql.ibd: 不存在(MySQL 5.7 正常)" echo "=== 验证完成 ==="

七、回退方案

7.1 回退触发条件

  • MySQL 启动失败且无法在 15 分钟内解决
  • 业务验证失败或数据不一致
  • 超过预定停机时间窗口

7.2 快速回退步骤

回退原理: 旧盘只是 umount 了,数据没动。只需把旧盘挂回 /data,启动 MySQL 即可。my.cnf 始终指向 /data/mysql,无需修改。

# 1. 停库(如果还启动着) systemctl stop mysql # 2. 卸载新盘,挂载旧盘回 /data umount /data # 卸载新盘 mount /dev/mapper/mysql-olddata /data # 旧盘设备路径按实际调整 # 3. 启动 MySQL systemctl start mysql # 4. 验证回退成功 mysql -uroot -p -e "SELECT @@datadir;" # 期望输出:/data/mysql/ # 5. 通知业务方回退完成

7.3 备份恢复(极端情况,两个盘都坏了)

# 使用 mysqldump 备份恢复 mysql -uroot -p < /tmp/mysql_full_backup_*.sql # 使用 XtraBackup 备份恢复 xtrabackup --prepare --target-dir=/tmp/mysql_backup_*/ xtrabackup --copy-back --target-dir=/tmp/mysql_backup_*/ --datadir=/data/mysql chown -R mysql:mysql /data/mysql systemctl start mysql

八、常见问题排查

8.1 启动失败:Permission denied / Can’t create test file

# 检查目录权限 ls -la /data/mysql/ # 修复权限 chown -R mysql:mysql /data/mysql chmod 750 /data/mysql

8.2 启动失败:InnoDB log file size mismatch

# MySQL 8.0.30 以下:检查 innodb_log_file_size grep innodb_log_file_size /etc/my.cnf # MySQL 8.0.30+:Redo Log 大小由 innodb_redo_log_capacity 统一控制 # innodb_log_file_size 已废弃(兼容期内仍可设,但会被忽略) mysql -e "SHOW VARIABLES LIKE 'innodb_redo_log_capacity';" # 如果需要重建 Redo Log: # MySQL 8.0.30 以下 rm /data/mysql/ib_logfile* # MySQL 8.0.30+ rm -rf /data/mysql/#innodb_redo/ # 修改对应参数后启动,MySQL 自动重建

8.3 启动失败:Tablespace is missing

# MySQL 8.0 特别检查 mysql.ibd(DD 字典文件,缺少则无法启动) ls -la /data/mysql/mysql.ibd

8.4 主从复制中断(主库迁移后)

前提: 主库迁移时保留了 auto.cnfserver_uuid 未变),且主库 binlog 未过期。此时从库 IO 线程只需停止后重启即可自动恢复,无需手动 CHANGE MASTER:

-- 从库执行:直接重启复制 -- MySQL 5.7 / 8.0(8.0.22 以下) STOP SLAVE; START SLAVE; SHOW SLAVE STATUS\G -- MySQL 8.0.23+ STOP REPLICA; START REPLICA; SHOW REPLICA STATUS\G

只有在以下情况才需要手动 CHANGE MASTER 重新指定位置:

  • auto.cnf 被排除导致主库 UUID 变化
  • 主库在停机期间发生了 binlog 轮转并已历史数据清理
-- 从库执行(仅当需要重新指定时) -- MySQL 5.7 / 8.0(8.0.22 以下) STOP SLAVE; CHANGE MASTER TO MASTER_HOST='<主库IP>', MASTER_PORT=3306, MASTER_LOG_FILE='<迁移前记录的 File>', MASTER_LOG_POS=<迁移前记录的 Position>; START SLAVE; SHOW SLAVE STATUS\G -- MySQL 8.0.23+ STOP REPLICA; CHANGE REPLICATION SOURCE TO SOURCE_HOST='<主库IP>', SOURCE_PORT=3306, SOURCE_LOG_FILE='<File>', SOURCE_LOG_POS=<Position>; START REPLICA; SHOW REPLICA STATUS\G

九、时间估算参考

数据量 在线全量 Rsync 耗时(参考) 停机阶段耗时(参考)
50 GB 30–60 分钟 2–5 分钟
200 GB 2–4 小时 3–10 分钟
500 GB 5–10 小时 8–18 分钟
1 TB 10–20 小时 15–35 分钟

注意: 实际时间取决于磁盘 I/O 速度、当前业务写入量。停机阶段已合并为一次(增量 rsync + 换盘位 + 启动验证),无需两次停机。


总结

  • my.cnf 全程不动: datadir=/data/mysql 从始至终有效,换的是底层盘,不是逻辑路径
  • 旧盘是天然回滚点: 只 umount 没删数据,出问题 mount 回去立刻恢复
  • auto.cnf 不能排除: 主库迁移排除会使 server_uuid 变化,所有从库主从断开
  • MySQL 8.0 的 mysql.ibd DD 字典文件,缺失直接无法启动
  • 旧盘保留至少 3 天观察期,不要立刻格式化

参考文档:

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论