
数据库迁移与演进 面试
数据库迁移与演进 面试
迁移策略
【困难】MySQL 到分布式数据库的在线迁移方案如何设计?如何保证零停机与数据一致?⭐⭐⭐⭐
🎯 目标等级:L3-L4 | ⏱ 建议用时:20 min | 🏷 标签:迁移策略 / 在线迁移
💎 关键结论
MySQL 到分布式数据库在线迁移的核心方案是双写 + 增量同步 + 灰度切换三阶段。第一阶段通过 CDC(Change Data Capture)工具实时同步增量数据;第二阶段双写验证,新旧库并行写入并比对;第三阶段灰度切流,逐步将读/写流量从 MySQL 迁移到目标库。全程业务零停机,回滚能力贯穿始终。
⚡ 记忆卡片
- 口诀:CDC 追增量,双写验一致,灰度切流量
- 关键词:CDC / 双写 / 灰度切换 / 数据校验 / 回滚方案
- 链路:全量迁移 → CDC 增量同步 → 双写验证 → 灰度切读 → 灰度切写 → 下线旧库
📖 核心知识
全量数据迁移:
- 使用批量同步工具(DataX、目标库官方工具如 TiDB Lightning/Dumpling)做全量导入;CDC 工具的快照能力(Debezium snapshot、Flink CDC 增量快照)也可完成全量初始化
- 大表分片并行迁移,记录迁移起始位点(binlog position)
- 迁移期间 MySQL 正常读写,不影响业务
CDC 增量同步:
- 从全量迁移开始的 binlog 位点开始,实时消费增量变更
- 常用工具:Canal(阿里开源)、Debezium(Red Hat)、Flink CDC、DTS(云厂商)
- 原理差异:Canal 伪装成 MySQL 从库拉取并解析 binlog;Debezium 以 Kafka Connect 连接器形态运行,对 schema 变更(DDL)捕获更完善;Flink CDC 在同一作业内衔接全量快照与 binlog 增量(增量快照算法,无锁、可断点续传)
- binlog 格式要求:行级捕获依赖 ROW 格式且
binlog_row_image=FULL——STATEMENT 格式只记录 SQL 原文,无法可靠还原行级变更;DDL 事件通常无法由 CDC 自动应用,需配合表结构版本管理人工执行 - 增量同步延迟控制在秒级,切换前需验证延迟趋近于零
双写验证:
- 应用层同时写入 MySQL 和目标库(或通过消息队列异步双写)
- 写入后对比两库数据,发现不一致立即告警
- 双写期间以 MySQL 为准(MySQL 为主,目标库为备)
- 写入顺序与失败补偿:先写主库再写备库;备库写失败不回滚主链路,记入补偿队列(本地消息表/MQ)异步重试,保证最终一致
- 异步双写的中间态:经消息队列双写意味着任一时刻两库数据不相等,切读前必须确认消费位点已追平;消费端需幂等(唯一键 + 版本号)防重复回放
- 并发覆盖防护:双写非原子,并发更新乱序到达会让目标库被旧值覆盖——以更新时间/单调版本号作条件写入(新者胜),或将同一主键的写入串行化到单一通道
灰度切流:
- 先切读流量:将部分读请求路由到目标库,验证读一致性
- 再切写流量:按租户/用户 ID 灰度,逐步将写入切换到目标库
- 灰度粒度选择:用户 ID 取模(流量均匀,适合 C 端)、按业务线/租户(隔离边界清晰,适合 B 端);某一键切流后只走单一写入通道,避免同一数据在两库同时可写
- 每阶段观察 1-3 天,确认无异常后扩大灰度比例
回滚方案:
- 切换后保留 MySQL 作为回滚库,通过反向 CDC 同步目标库变更回 MySQL
- 回滚的前提是反向同步链路持续可用:切流后的新增写入必须实时回流 MySQL,否则回滚会丢失新库产生的增量数据
- 回滚窗口建议 7-14 天,期间可随时切回 MySQL;窗口结束前执行一次全量对账再下线旧库
🔬 扩展知识
详情
- 【L3】数据校验工具:自研校验脚本按主键范围抽样比对,或使用 Percona pt-table-checksum 类似工具。关键表(金额、订单)必须全量校验。
- 【L3】三层对账体系与修复策略:行数比对(粗筛)→ 主键集合比对(发现丢失/多余行)→ 字段级 checksum(按行 MD5/CRC32 聚合,定位字段差异);迁移期跑全量校验 + 按更新时间的实时增量校验,切流后仍保留低频抽样对账。发现不一致时以主库为基准回补/覆盖修复,且先定位差异根因(同步 bug、并发覆盖、类型精度截断)再决定自动修复或人工介入。
- 【L4】停机窗口的真实来源:「零停机」的风险窗口不是切流动作本身,而是切流前「最后一次全量对账 + 增量追平」的时间——增量延迟趋近于零且终态全量对账通过后才能切写,该窗口应安排在业务低峰,并预设对账不通过时的推迟切换预案。
- 【L4】分布式事务一致性:如果目标库是分布式数据库(如 TiDB、OceanBase),需注意分布式事务与 MySQL 单节点事务的语义差异。切换前必须在目标库上验证核心业务的事务场景。
- 【L4】DDL 迁移:表结构变更(加字段、改类型)需在切换前完成,切换期间禁止 DDL。目标库的索引策略可能需要重新设计(特别是分布式数据库的分区键和局部索引)。
🏭 实战场景
详情
某电商平台从 MySQL 单库(数据量 500GB,日均写入 200 万条)迁移到 TiDB。方案:1)用 TiDB Lightning 全量迁移(耗时 4 小时);2)Canal 实时同步 binlog 增量(延迟 < 1 秒);3)双写验证 3 天,抽样比对 100 万条数据零差异;4)按用户 ID 尾号灰度:先切 10% 读流量观察 1 天,再切 10% 写流量观察 2 天,逐步扩大到 100%。全程业务零停机,回滚方案保留 MySQL 14 天。
⚠️ 常见误区
详情
常见误区:
- ❌ "全量迁移完就可以直接切换" → 全量迁移和切换之间可能有数小时的增量数据,必须通过 CDC 追平
- ❌ "双写会影响性能" → 异步双写(通过消息队列)对主链路性能影响 < 5%,且双写阶段通常只持续数天
- ❌ "切换后不需要回滚方案" → 生产环境必须保留回滚能力,至少 7 天回滚窗口
- ❌ "CDC 在任何 binlog 格式下都能同步" → 行级捕获依赖 ROW 格式且
binlog_row_image=FULL,STATEMENT 格式无法可靠还原行级变更,迁移前必须确认源库 binlog 配置 - ❌ "双写了两个库数据就强一致" → 双写非原子且异步链路存在中间态,一致性靠「主库顺序 + 补偿重试 + 对账校验」闭环保证,而非双写动作本身
🔀 发散问题
Q:CDC 同步延迟怎么办?
→ 监控 CDC 延迟指标,延迟超过阈值(如 10 秒)暂停切换。常见原因:目标库写入瓶颈、网络抖动、大事务阻塞。可通过并行消费和批量写入降低延迟。
Q:迁移过程中 MySQL 的 DDL 变更怎么处理?
→ 切换期间冻结 DDL,所有表结构变更推迟到迁移完成后在目标库执行。如果必须变更,需同步更新 CDC 映射规则。
【困难】传统单体数据库拆分为微服务数据库的核心挑战与渐进策略?⭐⭐⭐⭐
🎯 目标等级:L3-L4 | ⏱ 建议用时:15 min | 🏷 标签:迁移策略 / 微服务拆分
💎 关键结论
单体数据库拆分为微服务数据库的核心挑战是数据耦合:跨服务 JOIN、分布式事务、数据一致性。渐进策略分四步:1)识别限界上下文,划定数据归属;2)引入 API 层替代跨库 JOIN;3)用 Saga/事件驱动替代分布式事务;4)按服务逐步迁移表,双写过渡。切忌一步到位,应按业务域逐步拆分。
⚡ 记忆卡片
- 口诀:划边界替 JOIN,事件驱动替事务,逐步迁移双写过
- 关键词:限界上下文 / API 替代 JOIN / Saga / 事件驱动 / 双写迁移
- 链路:识别数据归属 → API 替代 JOIN → 事件驱动一致性 → 逐表双写迁移
📖 核心知识
识别限界上下文(Bounded Context):
- 按业务域划分数据归属,每个微服务拥有独立数据库
- 核心原则:一张表只属于一个服务,其他服务通过 API 访问
- 工具:事件风暴(Event Storming)工作坊识别领域事件和命令
替代跨库 JOIN:
- 单体中常见的多表 JOIN 查询,拆分后需改为 API 聚合
- 方案:BFF(Backend For Frontend)层聚合多个微服务 API
- 冗余字段:在需要频繁关联查询的场景,适度冗余关联数据(CQRS 读模型)
替代分布式事务:
- 微服务间数据一致性用 Saga 模式替代两阶段提交
- 编排式 Saga:中央协调器控制各服务本地事务和补偿操作
- 协同式 Saga:通过事件驱动,各服务监听事件执行本地事务
- 补偿设计三要求:可补偿性(每个正向操作都有对应逆向操作)、幂等性(补偿可能被重复触发)、防空补偿与悬挂(正确处理取消请求先于正向请求到达、正向操作实际未执行的时序异常)
订单服务创建订单 → 发布 OrderCreated 事件 → 库存服务扣减库存 → 发布 StockDeducted 事件 → 支付服务扣款 → 发布 PaymentCompleted 事件 → 如果任一步失败,触发补偿事务渐进式迁移步骤:
- 第一步:在单体内部按限界上下文划分模块,模块间通过接口调用
- 第二步:将目标表的数据通过双写同步到新数据库
- 第三步:将对应服务的读写流量切换到新数据库
- 第四步:下线旧表中的对应数据
🔬 扩展知识
详情
- 【L3】数据所有权原则:每个服务只能直接访问自己的数据库,禁止跨服务直连数据库。违反此原则会导致"分布式单体"。
- 【L3】共享表与公共数据处理:拆分后字典表/配置表不能再跨库 JOIN,方案是各库冗余副本 + binlog 订阅广播同步,或下沉为独立基础数据服务加缓存;这类公共数据的归属必须先行治理,否则拆分会被隐式耦合卡住。
- 【L3】查询侧的取舍:拆分后复杂查询(如报表)需要跨多个服务聚合数据。方案:CQRS 模式,将各服务数据同步到专用的查询数据库(Elasticsearch、ClickHouse)。
- 【L4】ID 策略:拆分后全局唯一 ID 需要统一方案(雪花算法、UUID、号段模式),避免 ID 冲突。
- 【L4】拆分实施顺序与回滚预案:先拆依赖少的边缘域(消息、日志类),核心交易域最后拆;每张表迁移遵循「双写 → 校验 → 切读 → 切写 → 清理」推进,旧表全程保持可写并接收反向同步,保证任一步可回滚。
🏭 实战场景
详情
某电商系统从单体 MySQL(200+ 张表)拆分为微服务数据库。拆分策略:1)识别 5 个限界上下文:用户、商品、订单、库存、支付;2)订单服务先拆分,将 orders/order_items 表迁移到独立数据库;3)引入 API Gateway 聚合订单和商品信息,替代原来的 JOIN 查询;4)库存扣减改为事件驱动(RocketMQ),替代原来的本地事务。拆分历时 4 个月,按服务逐步迁移,每个服务双写 2 周后切流。
⚠️ 常见误区
详情
常见误区:
- ❌ "一步到位全部拆分" → 拆分是渐进过程,每拆分一个服务都需要验证数据一致性和性能
- ❌ "拆分后还能用 JOIN" → 跨库 JOIN 不可行,必须用 API 聚合或冗余字段替代
- ❌ "分布式事务用 XA 就行" → XA 两阶段提交性能差、可用性低,生产环境推荐 Saga + 事件驱动
- ❌ "事件驱动最终一致就不会丢数据" → 消息可能丢失、重复、乱序,跨库必须配定时对账任务(行数 + checksum)兜底校验,发现不一致告警并修复
🔀 发散问题
Q:拆分后报表查询怎么处理?
→ 用 CQRS 模式将各服务数据通过 CDC 同步到专用分析库(ClickHouse、Elasticsearch),报表查询走分析库。
Q:拆分粒度怎么把握?
→ 按业务域(限界上下文)拆分,而非按表拆分。一个服务可以有多张表,只要它们属于同一业务域。过细的拆分增加运维复杂度和网络开销。
【中等】数据库选型从关系型到 NewSQL 的迁移评估维度有哪些?⭐⭐⭐⭐
🎯 目标等级:L2-L3 | ⏱ 建议用时:10 min | 🏷 标签:迁移策略 / 选型评估
💎 关键结论
关系型到 NewSQL 的迁移评估五大维度:1)数据规模与扩展需求(是否需要水平扩展);2)SQL 兼容性(现有查询能否平滑迁移);3)事务一致性要求(是否强一致);4)运维生态(团队技术栈、社区成熟度);5)成本(License、硬件、人力)。NewSQL(如 TiDB、CockroachDB、OceanBase)适合需要水平扩展 + SQL 兼容 + 强一致的场景。
⚡ 记忆卡片
- 口诀:规模 SQL 一致性,运维成本五维度
- 关键词:水平扩展 / SQL 兼容 / 强一致 / 运维生态 / 成本评估
- 链路:数据规模评估 → SQL 兼容性验证 → 事务一致性需求 → 运维能力评估 → 成本核算
📖 核心知识
数据规模与扩展需求:
- 单表数据量 > 5000 万或单库 > 1TB,考虑 NewSQL
- 需要在线水平扩展(加机器不停机),NewSQL 原生支持
- 如果数据量小且无扩展需求,传统 RDBMS 更合适
SQL 兼容性:
- NewSQL 通常兼容 MySQL/PostgreSQL 协议,但并非 100% 兼容
- 评估重点:存储过程、触发器、自定义函数是否支持
- 工具:用现有 SQL 在目标库做兼容性测试
事务一致性要求:
- NewSQL 支持分布式事务(强一致),但性能低于单机 RDBMS
- 评估:是否需要跨分片事务?事务并发量是否超出 NewSQL 吞吐上限?
运维生态:
- 团队是否具备 NewSQL 运维能力(故障排查、性能调优)
- 社区成熟度、文档完善度、商业支持
- 监控工具、备份恢复方案是否完善
成本评估:
- 开源/社区版阵营:TiDB(Apache 2.0)、OceanBase 社区版——License 免费,但需要更多机器资源(多副本 + 独立组件角色)
- License 形态需持续跟踪:CockroachDB 自 2024 年起改用 BSL(Business Source License),不再是 OSI 开源,生产使用需评估许可条款
- 商业版本(OceanBase 企业版、TiDB 商业支持等):收取 License/订阅费用,但提供商业支持与 SLA
- 对比传统 RDBMS 的垂直扩展成本 vs NewSQL 的水平扩展成本
🔬 扩展知识
详情
- 【L3】主流 NewSQL 对比:TiDB(兼容 MySQL,HTAP)、CockroachDB(兼容 PostgreSQL,全球分布)、OceanBase(兼容 MySQL/Oracle,金融级)。选型时优先验证 SQL 兼容性和事务语义。
- 【L3】迁移风险评估:先在测试环境用生产数据子集做功能测试和性能测试,重点关注慢查询、锁等待、分布式事务超时等场景。
- 【L4】选型论证顺序:先论证「现有 MySQL + 分库分表中间件不能满足」(扩容瓶颈频率、跨分片事务/JOIN 占比、中间件运维成本量化),再考虑 NewSQL——NewSQL 用单机事务性能与运维复杂度换透明扩展,数据量未达门槛时引入是负优化;PoC 必须用生产流量回放压测(慢查询、热点行、分布式事务占比)。
- 【L4】混合架构:不必全量迁移到 NewSQL,可以将热数据(需要水平扩展的表)迁移到 NewSQL,冷数据保留在 RDBMS,通过数据同步工具保持两边一致。
🏭 实战场景
详情
某金融系统从 Oracle 迁移到 OceanBase。评估维度:1)数据规模 10TB+,Oracle 单机扩展成本过高;2)OceanBase 兼容 Oracle 语法,现有 SQL 迁移改造量 < 10%;3)金融级强一致要求,OceanBase 支持 Paxos 协议多数派提交;4)团队有 MySQL 运维经验,OceanBase 运维模型类似;5)成本对比:OceanBase 方案总成本约为 Oracle 的 30%。迁移历时 6 个月,零停机切换。
⚠️ 常见误区
详情
常见误区:
- ❌ "NewSQL 可以完全替代 RDBMS" → NewSQL 在中小规模场景下没有优势,运维复杂度反而更高
- ❌ "NewSQL 兼容 MySQL 就是 100% 兼容" → 存储过程、触发器、特定函数可能不兼容,必须逐一验证
- ❌ "NewSQL 分布式事务性能和单机一样" → 分布式事务涉及多节点协调,延迟高于单机事务,高并发场景需要压测验证
🔀 发散问题
Q:NewSQL 和分库分表中间件(如 ShardingSphere)有什么区别?
→ NewSQL 在数据库内核层面实现分布式能力(透明扩展、分布式事务),中间件在应用层实现分片路由。NewSQL 运维更简单但迁移成本更高,中间件对应用侵入小但能力受限。
Q:什么时候不该用 NewSQL?
→ 数据量小(< 500 万行)、无水平扩展需求、团队无 NewSQL 经验、预算有限时,传统 RDBMS 更合适。