这事儿还得从头说起。前几天有个朋友半夜给我打电话,说公司数据库服务器磁盘告警,只剩 5% 的空间了,再不处理就会宕机。他手忙脚乱想迁移 MySQL 的数据文件,结果一查,发现坑比想象的多。其实很多 DBA 或者运维都遇到过这种场景——要么是磁盘空间不够,要么是换了新硬盘想把数据挪过去,或者单纯想把数据和日志分开存放。MySQL 数据文件迁移听起来是个简单操作,不就是把文件从一个目录搬到另一个目录嘛?但真做起来,细节能把人折腾死。

先说最基础的迁移场景,就是单纯换个数据目录。很多人第一反应是直接 cp 或者 rsync,然后改 my.cnf 里的 datadir 路径。但这里有个大坑——MySQL 在启动时会检查数据目录的权限和属主。如果你直接复制过去,新目录的 owner 可能不是 mysql 用户,或者 SELinux 上下文不对,MySQL 就会报错。我见过一个真实案例:有人迁移完数据、改了配置后重启服务,结果 MySQL 报“无法访问数据目录”,折腾半天才发现是父目录权限没改对。所以迁移前,最好先在新位置创建好目录,chown 成 mysql:mysql,然后停掉 MySQL 服务,再用 rsync 把旧数据同步过去。一定要用 rsync 而不是 cp,因为 rsync 能保留文件权限、时间戳,而且支持断点续传。同步完再改 my.cnf,重启即可。这套流程看着简单,但顺序错了就完蛋。
再往深里说,数据文件迁移不只是换目录那么简单。有时候需要考虑文件系统差异。比如从 ext4 迁移到 XFS,或者从本地磁盘迁移到 NFS 网络存储。MySQL 对文件系统很敏感,尤其是 InnoDB 引擎。InnoDB 依赖直接 I/O 和 fsync 来保证数据完整性,如果底层文件系统不支持这些特性,或者性能有问题,数据库就会变得又慢又不稳定。我见过有人把 MySQL 数据放在 NFS 上,结果每次写入都要等网络同步,业务一忙就卡死。更坑的是,NFS 的缓存策略和本地磁盘不同,服务器宕机时数据可能还没真正落盘。迁移到网络存储前,一定要测试 fdatasync、fallocate 等操作的表现,否则就是给自己埋雷。
还有一种常见情况,是只迁移某个表或某个库的数据文件。比如一个表特别大,占了好几个 GB,你想把它挪到另一个磁盘上。这时直接用 ALTER TABLE … TABLESPACE 或者 CREATE TABLE … DATA DIRECTORY 可能更靠谱。但要注意,MySQL 5.6 以后才支持单表空间独立文件,而且默认是开启的。如果你用的是共享表空间,迁移单个表就麻烦了,需要先执行 ALTER TABLE … DISCARD TABLESPACE,再复制 .ibd 文件,最后 IMPORT TABLESPACE。否则会出现表结构不匹配、数据丢失的情况。我有个客户就踩过这个坑——他直接复制 .ibd 文件,结果表结构对不上,数据全丢。所以操作前一定要查官方文档,弄清楚当前版本支持哪些功能。
说到迁移工具,很多人会想到 mysqldump。它确实稳,但慢得要命。如果你有几百 GB 的数据,用 mysqldump 导出再导入,可能要跑十几个小时,而且导入时会产生大量 binlog,磁盘压力剧增。更高效的做法是使用 Percona XtraBackup 或 MySQL 自带的克隆插件。XtraBackup 能实现在线热备份,拷贝物理文件,速度快得多。比如想迁移到新服务器,可以在旧服务器上运行 ,然后 rsync 到新服务器,再用 恢复。整个过程数据库不需要停机,业务照常跑。我上次帮一个电商平台迁移 1.2TB 数据,从阿里云 ECS 迁到自建机房,用 XtraBackup 只花了 4 小时,而 mysqldump 估计得一天一夜。不过物理备份也有要求——新服务器的 MySQL 版本必须和旧服务器一致,或者至少大版本相同,否则恢复时会报错。
迁移过程中最容易翻车的是外键约束和自增序列。比如直接复制表的数据文件,却忘了检查其他表是否有外键引用它,恢复后就会报外键冲突。更隐蔽的是自增 ID——如果导出时加了 选项,或者导入后表结构里的 AUTO_INCREMENT 值没有更新,新插入的数据可能会和旧数据冲突。我见过一个场景:有人从测试库往生产库迁移数据,结果生产库的某个表自增 ID 已经涨到 100 万,而测试库才到 10 万,迁移后没有重置自增值,新插入的数据直接覆盖了已有记录。所以迁移前一定要用 检查自增值,必要时手动调整。
说说迁移后的验证和回滚方案。很多人迁移完数据、改完配置、重启 MySQL 一看能连上,就以为万事大吉了。但可能数据根本没完全同步,或者索引损坏。我建议迁移完先跑一遍 ,检查所有表的数据完整性。然后再用 pt-table-checksum 对比新旧两边的数据,确保每行都一致。如果发现不一致,就需要回滚。回滚方案最好提前准备——比如保留旧数据目录不删除,或者用 LVM 快照做一次磁盘快照。我有次帮客户迁移,用 rsync 同步完数据后,发现新目录下的某个表文件大小比旧目录小了几百 KB,原来是 rsync 过程中网络中断导致的。幸好旧目录还在,重新 rsync 一次就解决了。所以千万别急着删旧数据,至少保留 24 小时,等业务稳定后再清理。
说到底,MySQL 数据文件迁移不是技术难题,而是细节的考验。每一步都要想清楚后果,每一次配置改动都要能回滚。就像开车换轮胎,看着简单,但没支好千斤顶就换,车砸下来可不是闹着玩的。如果你的 MySQL 数据非常重要,我建议先在小库上演练一遍,把各种异常情况都模拟出来——比如磁盘满了、网络断了、权限不对、版本不兼容。只有把这些坑都踩一遍,真正迁移时才能胸有成竹。毕竟,数据丢了,再牛的 DBA 也变不出魔法。


