当自建 MySQL 在一次流量高峰里暴露出主从延迟飙升、慢查询暴增、切换失败的连锁问题时,很多团队才真正下决心把核心库迁上云。数据库云原生化不是简单换个产品,尤其当被迁移的是订单这类不能停机的核心系统时,方案是否完善、回滚是否够快,往往比技术本身更决定成败。这篇文章把从自建 MySQL 迁移到 PolarDB 的完整路径拆开来讲:为什么迁、怎么选方案、怎么灰度切换、Spring Boot 要改什么,以及哪些坑容易在生产环境踩到。
先想清楚:自建 MySQL 到底卡在哪
推动迁移的通常不是某个单点故障,而是自建架构在规模上来后逐渐显现的几类结构性短板:
- 主从延迟是读扩展的天花板:自建 MySQL 基于 Binlog 的逻辑复制,大事务或高并发写入时从库容易追不上主库。延迟一旦拉大,所有"写后读"场景都有风险——用户刚下单却查不到订单,投诉随之而来。
- 运维负担偏重:主从切换依赖 MHA 等半成熟方案,参数调优要持续观察,版本升级往往需要停机,备份策略也要自己设计和验证,DBA 的精力大量消耗在日常救火上。
- 扩容不够快:纵向扩容要停机换规格,横向加从库要做全量数据复制,几百 GB 的库同步一次就要数小时,赶不上流量突增的节奏。
- 备份恢复时间不可控:即便配置了全量加增量备份,大库的完整恢复仍可能耗时数小时,对核心系统而言 RTO 偏高。
- 高可用并非万无一失:MHA 切换在理想情况下很快,但 SSH 超时、Binlog 不完整、从库 SQL 线程报错都可能让切换失败,每一次都可能升级成严重故障。
这些问题的共同根源,是"单机存储 + 逻辑复制"架构在云时代的先天限制。理解 PolarDB 的价值,得先理解它换了一种架构。
PolarDB 为什么能把主从延迟压到毫秒级
PolarDB 采用存储计算分离架构,这和自建 MySQL 有本质区别:
- 一写多读的物理复制:主节点负责写,通过 Redo 物理日志同步到只读节点。物理日志只记录数据页的修改,体积更小、解析更快,因此复制延迟通常能控制在毫秒级,而不是逻辑复制的秒级甚至更高。
- 共享存储:所有计算节点共享同一份底层存储,只读节点不需要各自维护一份数据副本,直接读共享存储的数据页。这意味着加只读节点不用复制数据,几分钟即可就绪,读扩展几乎不受复制延迟拖累。PolarDB MySQL 版最多可挂载 15 个只读节点。
- 协议兼容:SQL 语法、协议、驱动兼容主流 MySQL 版本,Spring Boot 应用大多只需改连接串,代码基本不动。
把这三点放在一起,就能解释为什么迁移后主从延迟、备份耗时、只读扩容时间往往有量级上的改善——差异来自架构,而不是简单的堆硬件。
三种迁移方案怎么选
迁移路径主要有三种,取舍点在于停机时间、回滚难度和数据量:
- DTS 全量 + 增量:全程零停机,靠增量同步维持一致性,回滚相对容易(可反向同步)。适合不能停机、需要随时回滚的核心业务,代价是要处理增量同步的性能边界。
- 结构迁移 + 数据集成批量导入:适合超大数据量,速度快,但需要额外的数据校验,回滚更复杂。
- 备份恢复:操作最简单,但停机时间到小时级,回滚困难,只适合可接受停机的非核心库。
对订单这类核心系统,通常会选 DTS 全量加增量——用零停机和可回滚能力换取略高一些的操作复杂度。判断依据其实很朴素:业务能容忍多长停机、出问题时要多快退回去,先回答这两个问题,方案基本就定了。
零停机迁移的五个关键步骤
整个迁移可以拆成五步,核心思路是"双写 + 灰度读 + 随时回滚"。
第一步:创建 PolarDB 集群并做兼容性评估。 规格选择上,主节点计算能力与源库对等、建议独享规格避免资源争抢,只读节点规格不宜低于主节点以免读性能瓶颈,存储用按需弹性扩展省去容量预估。迁移前务必检查源库特性是否兼容:
-- 检查非 InnoDB 存储引擎
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'order_db'
AND ENGINE NOT IN ('InnoDB', 'MEMORY');
-- 检查外键约束,确认迁移顺序
SELECT TABLE_NAME, CONSTRAINT_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'order_db'
AND REFERENCED_TABLE_NAME IS NOT NULL;
第二步:用 DTS 做数据迁移。 DTS 支持全量加增量的无缝衔接,增量延迟通常在秒级以内,是零停机的核心保障。全量阶段要注意限速,避免拖垮源库;实测中通过限速控制,源库 QPS 下降能压在个位数百分比内,增量阶段的影响则几乎可以忽略。迁移后不能只信 DTS 的表级行数对比,还要做业务维度的抽样校验:
-- 数据抽样对比:在源库和目标库分别执行并比对
SELECT MD5(GROUP_CONCAT(
id, user_id, order_status, total_amount, create_time
ORDER BY id
)) AS data_checksum
FROM order_db.orders
WHERE create_time >= '2026-06-01'
AND create_time < '2026-06-02';
第三步:Spring Boot 配置多数据源。 让应用同时连上源库和目标库,通过配置中心控制读流量比例和是否双写,这是灰度切换的基础:
migration:
# 读流量走 PolarDB 的比例:0=全部走 MySQL,100=全部走 PolarDB
read-polar-ratio: 0
# 是否双写:true 表示同时写 MySQL 和 PolarDB
dual-write-enabled: false
路由逻辑基于 AbstractRoutingDataSource,读操作按灰度比例随机路由,写操作按双写配置决定去向,切换过程无需重启应用。
第四步:流量灰度切换。 分阶段推进——先开双写确认 PolarDB 实时同步正常,再依次把读流量切到 10%、50%、100%,每一步都盯住响应时间、错误率和数据一致性,用 Nacos 动态下发配置:
# 阶段一:先开双写
curl -X POST "http://nacos:8848/nacos/v1/cs/configs" \
-d "dataId=migration-config&group=DEFAULT_GROUP&content=migration.dual-write-enabled=true"
# 观察 5 分钟确认写入无异常后,再切 10% 读流量
curl -X POST "http://nacos:8848/nacos/v1/cs/configs" \
-d "dataId=migration-config&group=DEFAULT_GROUP&content=migration.read-polar-ratio=10"
回滚必须一键完成、没有犹豫时间:把读比例打回 0、关闭双写即可立即退回 MySQL。要注意的是,回滚后需要用 DTS 反向同步这段时间写入 PolarDB 的数据。
第五步:原集群下线。 切到 100% 后不要急着下线,留足观察期并做全量数据终验(DTS 全库校验 + 业务对账),确认增量延迟归零并持续观察一段时间后,再按"停 DTS 同步 → 关告警 → 最终备份 → 保留数日后释放实例"的顺序收尾。
Spring Boot 适配:四个容易忽略的调整
代码虽然基本不动,但有几处配置值得针对性优化:
- 连接池参数:PolarDB 常跨可用区部署,网络延迟略高于本地自建,
connection-timeout要适当放宽;同时开启 HikariCP 的泄漏检测,帮助发现迁移期未关闭的连接。 - 事务隔离级别:先确认业务用的隔离级别(如 READ-COMMITTED)在 PolarDB 上行为与源库一致,尤其关注加锁行为,再决定是否需要调整。
- 批量操作:PolarDB 对超长 SQL 的解析开销和自建 MySQL 不同,单次批量插入行数建议控制在 500~1000,并开启 JDBC 的
rewriteBatchedStatements,让驱动层做批量优化。大表扫描则可借助并行查询加速。 - 读写分离:直接用集群 Endpoint,写请求路由到主节点、读请求自动分发到只读节点,应用侧不用自己实现分发逻辑,配一个连接串即可。
五个真实踩坑,值得提前规避
- DTS 增量同步延迟突增:灰度期开双写后,
INSERT ... ON DUPLICATE KEY UPDATE产生的 Binlog 事件量是普通 INSERT 的数倍,增量解析线程处理不过来导致积压。解法是把这类语句拆成先查后写,并适当提升 DTS 增量并发度。迁移期间尽量避免大批量 DML。 - 只读节点的"写后读"不一致:物理复制虽快但非零延迟,写完立刻读若被路由到只读节点就可能读到旧数据。对"写后立即读"场景,用集群 Endpoint 开启会话一致性(session consistency),或在事务内保证读写走同一连接,不要盲目依赖只读节点。
- 批量插入性能劣化:把自建 MySQL 上单次拼接数千行的 INSERT 直接搬过来,会因超长 SQL 解析拖慢 PolarDB。降低单次行数并开启批量重写后,性能反而优于原来。
- 存储过程动态 SQL 报错:PolarDB 对存储过程内的动态 SQL 有更严格的安全限制,需要通过参数显式开启。更稳妥的做法是迁移前完整评估存储过程、触发器、事件,并尽量把业务逻辑下沉到应用层。
- 大表 DDL 引发连接超时:大表 Online DDL 执行期间持有元数据锁,时间过长会让连接池的探活查询超时、连接被大量重建。建议低峰期执行,或使用无锁变更能力,应用侧也要做好连接超时容错。
迁移前的检查清单
动手前,把下面这些点逐一确认,能显著降低生产事故概率:
- 存储引擎是否全部为 InnoDB,字符集是否统一为 utf8mb4;
- 外键约束、自增列步长偏移、时区与 SQL_mode 是否与目标库对齐;
- 存储过程、触发器的兼容性,以及是否用到需要参数开启的特性;
- 单表千万行以上的大表,提前评估 DTS 迁移时间;
- 驱动版本、连接池参数是否适配,监控告警是否为 PolarDB 补齐。
写在最后
从自建 MySQL 到 PolarDB,本质是从"单机存储加逻辑复制"转向"存储计算分离"的架构升级,弹性扩展、毫秒级复制延迟、快照级备份这些能力都来自架构本身。但迁移的成败往往不在技术多先进,而在方案多完善、回滚多快速。
如果你正在评估这条路径,建议按"先评估、再验证、后落地"推进:用决策树和检查清单确认就绪,在测试环境完整走一遍灰度切换和回滚,再选低峰期正式开始,每一步都留好监控和回滚预案。谨慎推进,比追求速度更重要。