架构 (IT)系统架构师

如何在逐步迁移期间,在遗留的单体数据库与分布式微服务生态系统之间架构零停机时间的数据同步,同时保持ACID属性?

用 Hintsage AI 助手通过面试

问题的答案

问题的历史

从单体架构到微服务的演变创造了对逐步迁移策略的迫切需求。组织无法承担完全停止世界迁移的奢侈,尤其是那些在大规模操作的 OracleSQL Server 遗留系统中。这一问题源于现实场景,企业需要在不牺牲多年的历史数据完整性或接受数小时的维护窗口的情况下进行现代化。

问题

核心挑战在于跨多个领域的单体ACID事务与微服务的分布式特性之间的阻抗不匹配。当分解数据库时,会遇到分裂脑场景,即在遗留系统和新服务中同时进行更新。维持网络边界的引用完整性,同时保持两个系统的操作性,创造了一个分布式共识问题,而这个问题不能通过简单的数据库复制来解决。

解决方案

实施 事件驱动架构,利用 变更数据捕获(CDC)Outbox 模式 来确保可靠的事件发布。部署 Debezium 连接器,从遗留数据库事务日志中捕获行级更改,流式传输事件到 Apache Kafka 作为中央神经系统。同时,在微服务层中实施 Saga 模式 以处理分布式事务,确保最终一致性,同时保持每个服务的操作自主性。

生活中的情况

一家财富500强电子商务平台需要将其十年前的 Oracle 单体的订单管理系统迁移到基于 PostgreSQL 的微服务。库存、定价和订单履行模块在12个主要表中有着紧密的外键约束。在假日季节,系统每分钟处理50,000笔交易,对数据丢失或停机没有任何容忍。

解决方案A:双写策略

工程团队最初考虑修改遗留应用程序代码,以便同时写入 Oracle 和新的 PostgreSQL 服务。这种方法通过保持同步和一致的写入承诺了简单性。然而,这引入了灾难性的耦合风险——如果新的服务出现延迟或故障,整个遗留系统将崩溃。此外,通过 XA 协议 实施分布式事务将严重影响性能,可能在峰值负载期间将响应时间提高400%。

解决方案B:数据库触发器和视图

另一个选项是创建在 Oracle 中的数据库触发器,以便在行修改时直接调用REST端点。这看起来很有吸引力,因为它不需要应用程序更改。然而,这在数据库基础设施和网络拓扑之间创建了紧密耦合,使系统变得脆弱。如果微服务端点不可达,则触发器将失败,导致整个遗留事务回滚——这违反了零停机的要求。此外,当触发器依赖于特定的列结构时,管理架构迁移变得几乎不可能。

解决方案C:使用事件溯源的变更数据捕获

选择的架构利用 Debezium 监控 Oracle 的重做日志,捕获每次插入、更新和删除作为发布到 Apache Kafka 的不可变事件。微服务通过 Kafka Streams 消费这些事件,转换并将其持久化到 PostgreSQL,使用 Outbox 模式 来确保一次性语义。由 Confluent 管理的 Schema Registry 强制执行向后和向前兼容性,使用 Avro 架构。这将遗留系统与迁移复杂性解耦——Oracle 对新架构保持无知,而服务则按自己的节奏消费事件。

选择的解决方案及其理由

团队选择了解决方案C,因为它尊重 单一责任原则 并提供故障隔离。与双写不同,遗留系统性能不会受到微服务延迟的影响。与触发器相比,Debezium 异步运行而不阻塞事务。事件日志提供了不可变的审计跟踪,而 Kafka 的保留政策允许在架构演变期间重新处理微服务所需的历史数据。

结果

经过八个月的迁移,该平台成功移动了200TB的事务数据,99.97%的正常运行时间。系统在黑五期间处理的流量较去年降低了40%。当在新服务中发现定价计算bug时,团队从 Kafka 重新播放了三天的事件,未触及遗留的 Oracle 系统,纠正了230万个记录而没有停机。CDC管道现在作为实时分析的支柱使用 Apache Flink

候选人常常忽视的方面

当单体更改其表结构,而微服务消费CDC事件时,您如何处理架构演变?

候选人常常建议在迁移期间冻结架构,这对于敏捷企业而言是不切实际的。正确的方法是实施 Confluent Schema Registry,使用 Avro 架构,采用向前和向后兼容模式。当 Oracle 表发生更改时,Debezium 连接器将发布带有更新架构的事件,但注册处强制执行兼容性规则。服务应实施 Schema-on-Read 模式,使用 Apache Avro 的解析规则——忽略未知字段并对缺失字段使用默认值。此外,部署一个 CQRS 模式,使读取模型能够独立于源架构演变,使用 Kafka Connect 转换器在它们到达消费端点之前扁平化嵌套结构。

当两个系统在过渡期间同时更新同一实体时,会发生什么?

这会产生一个简单的时间戳无法解决的分裂脑场景。架构师必须实现 向量时钟CRDT(无冲突复制数据类型) 以实现确定性冲突解决。部署一个 双向同步 组件,消费微服务事件并使用 Kafka Connect JDBC Sink 写回 Oracle,但基于混合逻辑时钟的严格 最后写入胜出(LWW) 语义。

更重要的是,实施 领域驱动设计 边界——在迁移期间,每个聚合根只能由单体或微服务拥有写入权,决不能两者都拥有。使用 数据库标志Oracle 中指示迁移状态,通过使用 API 网关Strangler Fig 模式 适当地路由写入流量。

描述确保事务完整性的一种模式,当一个业务操作跨越遗留数据库和新微服务时。

大多数候选人错误地建议使用 双阶段提交(2PC) 在异构系统之间的分布式事务,这会导致脆弱的耦合和可用性问题。正确的解决方案采用 Saga 模式补偿事务。当用户操作需要更新 Oracle(遗留)和 PostgreSQL(新)时,通过在 CamundaTemporal 上构建的 Saga 调度器 来协调此过程。该过程顺序执行本地事务:首先更新 Oracle,然后发布域事件,然后执行微服务操作。如果任何步骤失败,执行补偿事务——如果微服务提交失败,则触发回滚事件,以便遗留系统消费以恢复 Oracle 的更改。这保持了最终一致性,而不在网络边界上锁定资源。