适用版本: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.cnf(server_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 天观察期,不要立刻格式化
参考文档:




