场景题:数据迁移解决方案

一、背景

随着业务发展,数据量大了之后,老服务器io压力大,需要迁移到新服务器上

在此之前,一般已经做过基础的前期优化:

  • 读写分离:主库写、从库读,分担读压力
  • 分库分表:按业务或哈希维度拆分数据,降低单库单表的数据量

当上述手段已经用尽,或者需要更换机房、迁移上云时,就需要对数据库本身进行迁移。

二、迁移方案分类

两种主流方案:

停机迁移:小公司,简单

不停机迁移:大厂,本文重点

三、不停机迁移整体设计

核心思路可以概括为:"全量 + 增量同步追平数据,开双写保证增量不丢,最后灰度切流平滑过渡"。具体分为以下阶段:

1.准备工作
  • 梳理迁移范围:明确哪些库、哪些表需要迁移
  • 在新环境创建对应库表:表结构、字段类型、索引需要与旧库对齐(引擎或版本不同时,还要评估类型兼容性)
  • 开发配套能力:双写模块、数据比对/订正程序、监控报警
  • 制定详细的迁移 checklist 和回滚预案:每一步都必须可回滚,且不能污染旧库
  • 在测试环境做多轮演练,覆盖同步中断、网络抖动等异常场景
2. 环境准备
  • 搭建新库实例:规格、网络、参数配置对齐生产标准
  • 提前上线双写开关、灰度开关(一般通过配置中心如 Nacos、Apollo 下发),初始状态为关闭,后续用配置变更而非重新发版来驱动流程
3.全量同步
  • 全量同步任务:将旧库存量数据同步到新库,记录同步日志,方便后续排查和补偿
  • 全量数据补偿:针对同步失败、超时的记录重新写入
  • 增量同步任务:全量同步启动后业务仍在持续写入旧库,这部分增量数据通常通过解析 binlog 的方式(如 Canal、Flink CDC 等组件)持续同步到新库,逐步追平差异
  • 增量补偿:处理增量同步中失败或延迟的数据

注意:如果表中存在自增字段,双写与增量同步并存的阶段要格外小心,避免同一条自增逻辑在旧库写入和增量链路里被重复计算,导致新旧库自增值不一致------实践中很容易踩坑。

4.全量数据校验与订正
  • 对比新旧库的记录数、字段值、行哈希等,确认一致性
  • 发现不一致时,以旧库数据为准,对新库执行删除 + 重新插入(或定向更新)进行订正
5.抽样校验
  • 定期从新旧库中抽取样本数据比对,确保后续双写运行期间一致性保持稳定
  • 可结合核心业务表、核心字段做针对性抽样,作为全量校验之外的常态化补充
6. 新版本发布(灰度能力预埋)
  • 停机迁移:停机后等待同步完成,直接切换即可
  • 不停机迁移:发布携带双写开关能力的新版本代码,此时开关仍是"只写旧库"状态,发布本身不影响现网
7. 开启双写

开关打开后,写请求同时写旧库和新库,写入策略如下:

  • 旧库写失败:整个请求视为失败,新库也不写------旧库在切流完成前始终是唯一权威数据源
  • 新库写失败:不回滚旧库的写入,只记录失败日志,供后续补偿程序处理
  • 新库失败率超过阈值:触发报警,视情况自动或人工关闭新库写入,避免故障扩大
8. 观察运行(短期)
  • 双写开启后先观察几天,确认双写本身不影响现有业务的性能和正确性
9. 持续观察(数周级别)

双写稳定运行数周,期间持续进行:

  • 数据一致性核对(全量 + 抽样)
  • 监控指标、报警阈值观察(QPS、延迟、慢查询、失败率等)
10. 灰度切流(读流量切换)
  • 圈定灰度名单,比如先让 1% 的用户读新库,其余仍读旧库
  • 观察无异常后逐步放量:5% → 10% → 20% → 50% → 100%
  • 每个阶段都保留快速回滚开关,出现异常立刻切回旧库
11. 关闭双写、下线校验补偿任务
  • 全量读流量切换到新库并稳定运行后,关闭双写
  • 下线数据比对、补偿等定时任务
12. 观察与下线旧库
  • 继续观察一段时间,确认新库独立承载业务没有问题
  • 确认无误后下线旧库(建议保留一段时间的数据备份兜底)

四、关键设计原则

  1. 全程可回滚:任意阶段出问题,都能快速回退到上一个稳定状态
  2. 旧库单一权威:切流完成之前,旧库永远是唯一真实数据源,新库的失败不能影响旧库
  3. 配置驱动、变更前置:双写开关、灰度开关提前上线、默认关闭,用配置下发而非重新发版来控制行为
  4. 小步快跑、充分验证:全量 → 增量 → 双写 → 灰度 → 下线,每一步走稳再进行下一步;没有绝对的万无一失,靠多层校验和补偿兜底

五、拓展题:迁移到云端的实践方案(以 AWS 为例)

上面的十二步方法论与云无关,是通用的迁移方法论。如果目标环境是 AWS,每个阶段都可以用对应的托管服务承接,从而减少自建组件的研发和运维成本。

5.1 通用方案与 AWS 服务的映射
通用方案环节 自建方式 AWS 对应能力
全量同步 + 增量同步 自研同步程序 / Canal / Flink CDC AWS DMS(Full Load + CDC)
数据校验 + 补偿 自研比对/订正程序 DMS 数据校验(Data Validation) + Data Resync
双写开关 / 灰度开关 自建配置中心(Nacos、Apollo 等) AWS AppConfig(功能开关 + 分阶段发布)
流量灰度切换 网关/服务层路由规则 ALB 加权目标组 / Route 53 加权路由 / AppConfig 百分比灰度
监控报警 自建监控系统 CloudWatch 指标 + 告警
网络连通 专线 / VPN Direct Connect / Site-to-Site VPN
目标库 自建 MySQL/PostgreSQL 集群 RDS / Aurora(MySQL/PostgreSQL 兼容)
5.2 网络与安全准备
  • 旧库若在自建机房或其他云上,首先要打通到 AWS VPC 的网络:数据量大、要求稳定低延迟优先选 Direct Connect (专线);时效要求不高、想快速搭建可以先用 Site-to-Site VPN(基于 IPSec 隧道)过渡
  • 规划好目标 VPC 的子网和安全组,只放通 DMS 复制实例与新旧库之间必要的端口,遵循最小权限原则
  • 权限和密钥:用 IAM Role 控制 DMS 的访问权限;数据库账号密码建议托管在 Secrets Manager ;传输链路开启 SSL/TLS,静态数据用 KMS 加密
  • 若是同构迁移 (如 MySQL → Aurora MySQL、PostgreSQL → Aurora PostgreSQL),一般不需要额外的 schema 转换;若是异构迁移(如 Oracle → Aurora PostgreSQL、SQL Server → MySQL),建议先用 Schema Conversion 能力跑一次评估报告,看自动转换比例------存储过程、视图等对象的人工改造工作量往往比数据搬迁本身更大,需要提前预留时间
5.3 全量 + 增量同步:AWS DMS 落地

把通用方案里"全量同步任务 + 全量补偿 + 增量同步任务 + 增量补偿"这一整块,用 DMS 一套服务承接:

  • 创建复制实例(Replication Instance) ,根据数据量和吞吐需求选择合适规格;正式迁移建议开启 Multi-AZ,提高复制实例本身的可用性
  • 配置好源端点(旧库)和目标端点(新库,对应 RDS/Aurora)
  • 创建迁移任务,类型选择 Full Load + CDC :先做全量装载,装载完成后自动切换到基于事务日志的增量捕获
    • MySQL 源依赖行级 binlog
    • PostgreSQL 源依赖逻辑复制槽(logical replication slot)
    • 原理上和自建方案里用 Canal、Flink CDC 解析 binlog 做增量同步是一致的,只是 DMS 把这部分能力托管化了
  • 需要注意,DMS 的 CDC 并不是严格意义上的实时同步,存在一定延迟,延迟大小受源端负载、网络状况、复制实例规格、目标端写入能力等因素影响,官方也不对 CDC 延迟提供 SLA;业务对同步延迟有严格要求时需要提前压测评估
  • 性能优化建议:
    • 全量装载阶段可以先关闭二级索引和外键约束,装载完成、进入 CDC 前再补建索引和约束,避免装载过程中的索引维护开销
    • 迁移到 RDS/Aurora 作为目标时,切换前可以先关闭目标库的自动备份和 Multi-AZ,减少装载阶段的额外开销,等真正切流前再打开
    • 大表可以按日期分区等维度拆成多个任务并行跑,或对有单列主键/唯一索引的表启用并行装载
    • 留意对源库的影响:全量装载阶段 DMS 会对源表做全表扫描,增量阶段也会持续读取事务日志,必要时通过拆分任务、降低并发来减少对生产库的压力
5.4 数据校验与订正:DMS 数据校验 + 自定义方案
  • DMS 内置**数据校验(Data Validation)**能力:开启后,全量装载完成即开始逐行比对源和目标;CDC 阶段持续比对增量变化。普通字段直接比较值,LOB 大字段用校验和(checksum)比较;数据量大的表会按主键切分成多个分片并行比对
  • 校验失败的记录会被专门记录下来,方便定位具体是哪些字段不一致
  • DMS 还提供 Data Resync 能力:检测到不一致时可以自动从源端重新拉取数据写入目标,这基本对应通用方案里的"补偿"环节,能省下不少自研补偿逻辑的工作量
  • 若校验时效性要求很高,或数据量太大导致内置校验拖累迁移速度,可以把校验单独拆成一个"仅校验任务",跑在独立复制实例上,与迁移任务解耦
  • 抽样校验这一层,仍建议保留一套自己的定时抽样比对脚本(比如定时查询源和目标的行数、聚合值),作为 DMS 原生校验之外的双重保障,尤其是针对核心业务表
5.5 双写开关与灰度切流:AWS AppConfig

通用方案里的"双写开关""灰度白名单切流"本质上是一种功能开关(feature flag)+ 分阶段发布(gradual rollout)能力,AWS 上可以用 AppConfig 承接:

  • 把"是否开启双写""新库读流量灰度比例"定义成 AppConfig 里的 feature flag,应用代码定时拉取最新配置值
  • 灰度切流时,用 AppConfig 的分阶段部署策略:设定总时长和每一步的放量百分比,让灰度比例按设定节奏自动爬升;同时把关键业务指标接入 CloudWatch 报警,一旦触发报警,AppConfig 可以自动把配置回滚到上一个版本,不需要人工紧急操作
  • 若灰度切流发生在服务/网关层,而不是应用内部按用户维度区分读写库,也可以用 ALB 的加权目标组,或 Route 53 的**加权路由(Weighted Routing)**在 DNS 层面按比例分流到新旧两套服务集群------这在 AWS 蓝绿发布场景里很常见,思路和应用层灰度是相通的:小比例验证 → 逐步放量 → 出问题随时切回
5.6 监控与告警:CloudWatch
  • DMS 复制实例和任务的关键指标(CDC 延迟、吞吐量、失败记录数等)都能在 CloudWatch 里查看,建议针对 CDC 延迟、任务失败率设置告警阈值
  • RDS/Aurora 自身的 CPU、连接数、慢查询、复制延迟等指标同样通过 CloudWatch 监控
  • 结合 SNS,可以把告警实时推送到企业内部的 IM 群或邮件,对应通用方案里"超过阈值就报警"的诉求
5.7 迁移后架构优化建议
  • 若目标是 MySQL/PostgreSQL 体系,优先考虑 Aurora 而非普通 RDS:存储计算分离架构,读写性能和扩展性通常更好,并原生支持多个只读副本
  • 结合分库分表后的实际读写比例,评估是否需要增加只读副本分担读流量
  • Multi-AZ 部署保障高可用;有跨地域容灾诉求时可进一步评估 Aurora Global Database
  • 迁移完成、旧库下线后,记得清理迁移期间创建的 DMS 资源(复制实例、任务等),避免产生不必要的持续费用。DMS 本身只对复制实例和额外日志存储收费,数据传输免费,长期使用还可以考虑用 Database Savings Plans 进一步降低成本
5.8 上云迁移的额外注意事项
  • DMS 要求源端或目标端至少有一端在 RDS 或 EC2 上,不支持纯粹的"本地机房到本地机房"复制;若是云内迁移(比如从自建在 EC2 上的数据库迁移到 RDS),这一点通常不是问题
  • 若涉及异构引擎迁移(更换数据库产品/引擎),schema 转换和存量存储过程改造往往比数据搬迁本身更耗时,需要提前拉长评估周期,不要只按数据量估算迁移工期
  • 大字段(LOB)迁移对性能影响较大,需要提前梳理哪些表有大字段列,合理设置 LOB 处理模式,避免因截断导致校验失败
  • 曾用于迁移前存量数据库盘点的 Fleet Advisor 工具已于 2026 年 5 月起停止支持,若需要做迁移前的现状摸底,需改用官方最新的评估方式或人工梳理
  • 建议先挑一张非核心的小表完整跑一遍"准备 → 全量 → 增量 → 校验 → 灰度切流"全流程,验证工具链和性能符合预期后,再对核心表做正式迁移

六、总结

不管是自建方案,还是搬到云上用 DMS + AppConfig + CloudWatch 这套组合拳,核心思路没有变化:全量追平存量、增量追平变化、双写保障不丢数据、灰度切流平滑过渡、每一步可验证可回滚。云厂商托管服务能省掉很大一部分基础设施自研和运维成本,但迁移过程中的方案设计和风险控制思路是通用的------这也是这类系统设计题目在面试中反复被问到的原因:考察的不是某个具体工具怎么用,而是能不能把一件"看似简单"的数据搬迁做到万无一失。

相关推荐
认真的酒窝9 小时前
自己动手开发编译器(十一)语义分析
java·开发语言
wbs_scy11 小时前
Linux C++ 高并发编程:线程池全链路深度解析,从原理到手撕实现
java·开发语言
JAVA面经实录91711 小时前
Linux 常用命令完整知识体系
java·linux·开发语言·汇编
贪玩的蛋挞11 小时前
C#与闭包
开发语言·c#
端庄的战斗机12 小时前
javascript 设计模式(文章很长,请自备瓜子,水果和眼药水)
开发语言·javascript·设计模式
Full Stack Developme12 小时前
Java LRU 与 LFU 算法及应用
java·开发语言·算法
C语言小火车14 小时前
C++ 堆排序深度精讲:基于完全二叉树的选择排序进化,最坏情况 O(n log n) 的稳定王者
开发语言·c++·算法·排序算法·堆排序
北冥you鱼14 小时前
abigen 最佳实践:从入门到精通,高效生成 Go 语言合约绑定
开发语言·golang·区块链
ALex_zry15 小时前
C++26 std::complex 结构化绑定详解:auto [re, im] = c
c语言·开发语言·c++