数据库迁移这事儿,干过的都知道有多头疼。尤其是那种线上业务跑着跑着,突然跟你说“明天要把数据库搬到新服务器上,还不能停”。我当时第一反应是:这是让我在高速公路上换轮胎啊。但干久了你会发现,零停机迁移不是玄学,背后就是那几套固定的打法。今天这篇指南,就把我从踩坑到总结出来的套路掰开揉碎了讲给你听,从方案设计到性能调优。

先说最核心的问题:怎么做到零停机?关键点在于“切换”那一步。你不能直接停掉旧库,把数据拷过去,再开新库。那样用户得骂娘。正确的做法是搭建一个主从复制关系。先把新服务器配成旧库的从库,数据实时同步过来。等同步追上之后,再把应用层的读写流量切到新库。这一步需要你提前在应用层或者中间件层做个读写分离或者动态切换策略。比如用HAProxy或者Nginx做数据库代理,切换时改下配置就行,不用重启应用。我见过最土的办法是在代码里写个开关变量,改完重启应用,那其实也算停机,只是时间短了点。真正的零停机,是用户完全感知不到。
那数据同步过程中,最怕什么?怕的是旧库还在跑业务,新库同步赶不上。尤其是大表,几亿行数据那种,全量同步可能要跑好几个小时。这期间旧库产生的增量数据,如果同步机制慢,就会越拉越远。解决方法是分两步走:先做全量同步,再做增量同步。全量同步用工具比如mysqldump或者pgdump,加上--single-transaction参数,保证导出时一致性。导出完了导入到新库,这时候新库已经有一个静态快照。然后配置binlog或者WAL日志的实时同步,把从全量导出开始到切换那一刻的增量数据补上来。等到新库的延迟降到几秒以内,就可以准备切换了。我一般会让同步跑满24小时,确认稳定再动手,别急着切。
切换那一刻,还有个陷阱叫“数据一致性”。你以为同步追上就完事了?不一定。如果旧库在同步过程中有事务没提交,新库可能还没拿到。所以切换前,要先把旧库的写入停掉,等所有事务都刷完,再检查新库的同步位点。具体做法是在旧库上执行FLUSH TABLES WITH READ LOCK,或者用SET GLOBAL readonly=1,把旧库设为只读。这时候应用层的写入请求会失败,但读还能正常跑。然后你马上检查新库的同步状态,确认延迟为0,再把应用的写流量切过去。整个过程可能只有几十秒的写阻塞,但读完全不受影响。对用户来说,最多就是发个评论失败了,刷新一下就能继续。比起直接停机半小时,体验好太多了。
迁移完了不等于完事,性能调优才是重头戏。新服务器的配置可能跟旧的不一样,比如换了SSD,内存翻倍了,CPU核心数也变了。这时候数据库的参数要重新调。最典型的是innodbbufferpoolsize,以前内存小可能只设了8G,现在内存128G,你得设到80G以上。还有连接数maxconnections,新服务器并发能力更强,可以适当调高。但别盲目,调完要压测。我见过有人直接照搬旧配置,结果新服务器性能比旧还差,后来发现是磁盘IO调度策略没改,用的还是默认的cfq,换成deadline或者noop之后,性能直接翻倍。新硬件要配新参数,别偷懒。
还有一个很多人忽略的点:索引和查询优化。迁移是个好机会,顺便把旧库里的烂索引清理掉。你可以用pt-query-digest或者慢查询日志,找出那些执行频率高但索引没覆盖的SQL,重新设计索引。比如有个订单表,旧库上有个联合索引(a,b,c),但实际查询只用a和c,那这个索引就是浪费空间,还拖慢写入性能。迁移后可以改成(a,c)或者加个覆盖索引。我干过一次大迁移,顺便把全库索引重构了一遍,结果写入性能提升了30%,查询响应时间从200毫秒降到50毫秒。别心疼那点分析时间,值。
说完性能,再说说监控。迁移完的头几天,你得盯着新库的指标看。关键指标包括:磁盘IOPS、CPU使用率、连接数、慢查询数量、主从延迟(如果还有从库)。这些最好用Prometheus加Grafana搭个面板,实时看。我习惯设几个告警阈值:CPU超过80%持续5分钟告警,慢查询超过每秒10条告警,磁盘IO等待时间超过100毫秒告警。一旦触发,马上查原因。有一次迁移完第二天,CPU飙到90%,排查发现是某个报表查询用了全表扫描,建个索引就解决了。如果没有监控,可能用户投诉了才发现,那就被动了。
还有一个容易翻车的地方:应用层的连接池。迁移后新服务器的IP或者端口变了,应用端的连接池配置得跟着改。比如Java的HikariCP,配置里要写新的jdbc url。但连接池里可能会缓存旧的连接,导致切换后一段时间内应用还在连旧库。解决办法是在切换前先改配置,然后重启应用。如果不想重启,可以用连接池的动态刷新功能,比如HikariCP的resetConnection方法。更稳妥的做法是在数据库代理层做切换,应用只管连代理,代理再转发到实际数据库。这样切换对应用完全透明,改代理配置就行,不用动应用。
说说回滚方案。迁移这东西,永远要留后手。万一新库上线后出问题,比如性能不达标或者数据不一致,你得能快速切回旧库。所以迁移完成后,旧库别急着销毁,保持主从关系或者干脆让旧库继续运行一段时间。我一般会保留旧库至少一周,每天做一次数据校验,对比旧库和新库的行数和checksum。如果发现问题,立刻切回旧库,修复后再重新迁移。回滚的操作跟切换类似,先把写流量切回旧库,再重新搭建从库同步。这一步看起来麻烦,但真出了事,它能救你命。我见过有人迁移完第三天发现丢了数据,旧库已经删了,只能从备份恢复,损失了整整两天的业务数据。那种教训,一次就够了。
数据库迁移这事儿,说到底就是“准备”两个字。方案设计、数据同步、切换步骤、性能调优、监控告警、回滚预案,每一步都要提前想清楚。别指望临场发挥,数据库不会给你第二次机会。零停机听起来高大上,但拆开来看,就是做好每一个细节。你把这些细节都抠到位了,迁移就是一次平稳的升级,而不是一次心惊胆战的冒险。下次再遇到迁移任务,别慌,按这套流程走,稳得住。


