干我们这行的,最怕听到的一句话就是“数据库要迁移”。尤其是MySQL,平时跑得好好的,一说到搬家,各种幺蛾子就来了。数据量小还好说,几十个G的库,导出导入也就一顿饭的功夫。但要是碰上几个T的大库,或者业务不能停的线上环境,那真是一步一个坑,踩进去就是事故。今天这篇不整虚的,把我在生产环境摸爬滚打总结出来的MySQL迁移步骤,掰开揉碎了讲给你听,照着做,至少能让你少掉几把头发。

第一步,也是最容易被忽略的一步,不是敲命令,而是盘家底。你得先搞清楚源库到底有多大,有哪些库哪些表,表引擎是什么,有没有触发器、存储过程、自定义函数这些“附属品”。很多人直接mysqldump一把梭,结果导到一半发现有个表几百个G,或者恢复的时候报错说找不到某个函数,那时候再回头补就晚了。我的习惯是,先查informationschema,把库表大小、行数都列出来,心里有个底。同时,用mysqldump --no-data先导一份结构出来,检查一下有没有特殊字符、保留字做表名的情况,这些细节在迁移时最容易引发兼容性问题。
接下来就是选择迁移工具和方式。别迷信单一工具,场景不同,方案天差地别。如果你的库不大,停机窗口也够长,那我建议就用最经典的mysqldump。命令很简单,但参数有讲究。一定要加上--single-transaction,这样InnoDB表能拿到一致性快照,备份过程中不会锁表,业务还能继续写。同时加上--set-gtid-purged=OFF,如果你用的是GTID复制,这个参数能避免导入时产生GTID冲突。如果数据量超过50G,mysqldump的效率就有点捉襟见肘了,这时候物理备份工具xtrabackup是更好的选择。它直接拷贝数据文件,速度是逻辑备份的好几倍,而且备份时对业务影响极小。但注意,xtrabackup备份的是整个实例,没法单独挑几张表出来,恢复时也需要在目标机上进行prepare操作。
说完工具,咱们聊点实际的。假设你选了mysqldump,导出命令敲完之后,千万别急着导入。先检查导出的SQL文件大小,再打开文件头看看有没有警告信息,比如“Warning: Using a password on the command line interface can be insecure”这种,虽然不影响数据,但说明你的命令可能被shell历史记录了,存在安全隐患。导入的时候,也有讲究。目标库的字符集、排序规则一定要和源库一致,否则中文乱码、排序错乱是常有的事。导入前先把binlog关掉,或者设置sqllogbin=0,不然导入过程中产生的binlog会白白占用磁盘空间,拖慢速度。还有个坑,是maxallowedpacket参数,默认4M,如果你的表里有大的BLOB字段,导入时经常会报“Packet too large”错误,记得提前把这个参数调大,比如改成64M或128M。
数据导进去只是第一步,真正的考验在后面。导入完成后,别急着切流量,先做数据校验。最简单的方法,是分别统计源库和目标库的表行数,用count(*)跑一遍,比对结果。但表多了这个办法太慢,更高效的方式是查informationschema.tables,对比每个表的行数和数据大小。如果发现不一致,再单独针对那张表用checksum table去校验。这里要提醒你,checksum table在MyISAM和InnoDB上的算法略有不同,跨引擎迁移时校验结果可能对不上,这时候要以实际查询结果为准,别被校验值误导了。除了数据本身,权限也得重新刷一遍。MySQL的用户权限是存在mysql库里的,mysqldump默认不会导出用户和授权信息。你得单独导出mysql.user、mysql.db、mysql.tablespriv这些表,或者用pt-show-grants工具把授权语句生成出来,然后在目标库上重新执行。这个环节漏了,等业务连上去才发现账号没权限,又是一阵鸡飞狗跳。
如果你的业务不允许长时间停机,那就得用增量同步的方式了。方案也不复杂,核心思路是:先做一次全量备份恢复到目标库,然后利用MySQL的binlog,把从全量备份时间点之后的所有变更,在目标库上重放一遍。具体操作是,全量备份时记录下binlog的position,恢复完成后,在源库上把从那个position开始到当前时刻的binlog解析成SQL,用mysqlbinlog工具导出,再导入到目标库。这个过程中,源库的binlog格式最好是ROW模式,因为STATEMENT模式在某些不确定函数(如NOW()、UUID())上会导致主从数据不一致。还有,binlog的过期时间要调长一点,别迁移到一半,binlog被自动清理了,那可就前功尽弃了。增量同步跑完后,两边数据依然可能存在微小的差异,比如几秒的未提交事务,这时候就需要短暂地停一下写操作,把最后的binlog也追上,然后才算真正追平。
最后一步,切换之前,一定要演练一遍回滚方案。很多人觉得迁移成功就万事大吉,结果第二天发现业务有个隐藏的兼容性问题,想回滚却发现源库已经被改得面目全非。我的做法是,迁移完成后,先保留源库只读权限,观察目标库运行一周,确认稳定后再把源库下线。这一周里,每天跑一次数据对比脚本,一旦发现异常,立刻切回源库。另外,别忘了检查目标库的配置文件。比如innodbbufferpoolsize,如果你原来机器是64G内存,配置了32G的buffer pool,新机器只有32G内存,那你得把参数调成16G甚至更小,否则MySQL启动就会失败,或者频繁OOM。还有,skip-name-resolve这个参数,源库如果开了,目标库忘了开,那连接时DNS反查慢的问题又会冒出来,表现为连接偶尔超时。
写到这里,也该收个尾了。MySQL迁移这事儿,说难也难,说简单也简单,核心就四个字:稳、准、狠、细。稳是心态要稳,别急;准是数据要准,校验到位;狠是遇到问题下手要狠,该回滚就回滚;细是每个参数、每个步骤都要过脑子。我见过太多人栽在细节上,比如忘了改时区,导致时间字段差了8个小时;又比如源库用了latin1,目标库用了utf8mb4,导入后中文全变成问号。这些坑,说多了都是泪。但只要你按着上面这套流程走,至少能把风险降到最低。记住,迁移不是目的,业务稳定才是。下次再接到迁移需求,深呼吸,盘家底、选工具、导数据、做校验、搞同步、备回滚,一步步来,没什么大不了的。


