干了这么多年数据库运维,最怕听到的一句话就是“把库迁一下,别丢数据”。每次听到这个,后背都发凉。MySQL迁移这事儿,看着简单,不就是把数据拷过去嘛,可真要动起手来,坑一个接一个。今天就把我这些年踩过的坑、总结的经验,一次性倒出来。

先说最基础也是最重要的一个认知:迁移不等于拷贝。很多人觉得,我把data目录打包,传到新机器上解压,完事儿。这种想法害死人。且不说MySQL版本差异可能导致数据文件格式不兼容,就算版本一模一样,你直接拷贝数据文件,万一源库还在写入,拷贝出来的数据就是残缺的,甚至整个表都打不开。我自己就吃过这个亏,当时为了图快,直接tar了数据目录,结果新库启动后一堆表报错,只能从备份里恢复,白白浪费了半天时间。所以,任何迁移方案,第一步永远是确保数据一致性,要么停库拷贝,要么用工具保证一致性。
那停库迁移行不行?行,但代价大。如果你的业务允许停机,比如深夜维护窗口,那最简单粗暴的方式就是停掉MySQL,然后把整个数据目录打包拷走,再在新机器上启动。这种方式的好处是绝对一致,不会出现任何数据错乱。但问题也很明显,停机时间取决于数据量大小,几GB的数据还好,要是几个TB,光拷贝就得几个小时,业务根本等不起。而且,这种物理拷贝方式对操作系统、MySQL版本都有要求,跨大版本迁移时,即使数据文件能识别,系统表结构也可能不兼容,到时候启动都成问题。
那不停机怎么迁移?这时候就要请出mysqldump这个老朋友了。mysqldump逻辑备份,生成SQL语句,然后在目标库执行这些SQL来重建数据。它的优势是跨版本、跨平台兼容性好,因为备份出来的是文本文件,只要SQL语法能识别,基本都能导入。具体操作也简单:mysqldump -u用户名 -p密码 --single-transaction --master-data=2 数据库名 > 备份.sql。注意这个--single-transaction参数,它利用InnoDB的事务特性,在不锁表的情况下获得一致性快照,适合线上环境。加--master-data=2,会在备份文件里记录binlog位置,方便后续做主从同步或者增量恢复。
但mysqldump也有它的短板。数据量一旦上到几十GB甚至上百GB,mysqldump导出再导入的过程会非常慢,因为要一条条执行SQL,插入语句还要维护索引。我见过一个项目,200GB的库,mysqldump导了将近三个小时,导入又花了五个小时,整个迁移窗口接近一天。而且,在大数据量下,mysqldump生成的SQL文件体积巨大,传输和存储都是问题。所以,如果你的库超过50GB,我建议直接放弃mysqldump,考虑用物理备份工具。
物理备份工具里,Percona XtraBackup是当之无愧的首选。它直接备份InnoDB的数据文件,通过redo log保证备份的一致性,备份速度比mysqldump快一个数量级。用法也不复杂:xtrabackup --target-dir=/backup/mysql,然后xtrabackup --prepare --target-dir=/backup/mysql,把备份目录拷到新机器上,启动MySQL。整个过程不需要停库,对线上业务几乎无影响。我上次迁移一个800GB的库,用了不到四十分钟就完成了备份,恢复也就半个多小时,效率是mysqldump没法比的。
不过XtraBackup有个坑,就是版本兼容性。它必须和MySQL版本严格对应,比如MySQL 5.7要用XtraBackup 2.4,MySQL 8.0要用8.0系列,混用会直接报错。另外,XtraBackup对内存和磁盘IO要求较高,备份过程中会占用一定系统资源,如果服务器本身负载就高,可能会影响线上业务性能。所以用之前,最好先压测一下,看看资源占用情况。
说完工具,再说说迁移过程中最容易忽视的一个环节:字符集和排序规则。很多人在迁移后遇到乱码问题,排查半天发现是源库和目标库的charactersetserver不一致。比如源库是utf8mb4,目标库默认是latin1,导入数据后中文全变成问号。解决办法是在导入前先检查并设置好目标库的字符集,最好在配置文件里显式配置character-set-server=utf8mb4和collation-server=utf8mb4unicodeci,同时导入时加上--default-character-set=utf8mb4参数。别嫌麻烦,这一步骤能避免后续大量返工。
还有一个细节容易被忽略:自增主键的偏移。如果你迁移后还要继续写入数据,而源库的表自增ID已经到100了,目标库导入后如果没设置好,可能会从1开始,导致主键冲突。mysqldump的备份文件里其实包含AUTO_INCREMENT信息,导入时会自动带上,但用XtraBackup物理恢复时,这个信息是包含在表结构里的,一般没问题。不过为了保险起见,迁移完成后最好抽查几个大表的自增值,确认无误再切换流量。
切换流量这个环节,我建议分两步走。第一步,先做一次全量迁移,然后在目标库上配置主从同步,让新库实时追上源库的增量数据。第二步,等确认主从同步延迟为0,且数据完全一致后,再手动切换写的流量。这样做的好处是,万一切换后发现有问题,还能快速回退到源库,不用承担数据丢失的风险。我见过不少团队,图省事直接停库迁移,结果切过去发现业务有兼容性问题,想回退又发现源库已经删掉了,只能硬着头皮修复,那种压力真不是一般人能扛的。
别忘了验证数据完整性。迁移完成后,别急着让业务方确认,自己先跑一遍数据校验。简单点的做法,对比源库和目标库的表数量、总行数、总字节数;严谨点的,可以针对关键表做count(*)对比,或者用pt-table-checksum这类工具做逐行校验。我习惯迁移后随机抽几个表,分别查一下最大ID、最小ID、总行数,再对比一下关键业务表的sum值,基本能发现大部分问题。校验这一步,千万别省,很多隐性数据问题都是在这时候暴露的。
把上面这些流程走完,一次MySQL全量迁移才算真正落地。说实话,迁移这事儿没有银弹,每个环境都有自己的特殊性,但核心思路是一致的:先确保数据一致性,再考虑迁移效率,做好验证和回滚准备。工具选择上,小库用mysqldump,大库用XtraBackup,各有各的适用场景。字符集、自增ID、主从同步这些细节,每一个都可能成为翻车点。只要把这些点都照顾到,一步到位不丢数据,完全做得到。下次再有人让你迁库,心里就有底了。


