首页
/ Milvus CDC 跨集群复制(Replication)技术总览:主备灾备拓扑、角色约束与故障切换机制

Milvus CDC 跨集群复制(Replication)技术总览:主备灾备拓扑、角色约束与故障切换机制

2026-09-08 23:00:35作者:贡沫苏Truman

本文以 docs/design-docs/design_docs/cdc/user_docs/01-cdc-replication-overview.md 为骨架,结合 Milvus 仓库中 CDC 模块源码与配套运维文档展开。

Milvus CDC(Change Data Capture,变更数据捕获)用于将一个 Milvus 集群的数据变更持续复制到另一个 Milvus 集群。从 Milvus v2.6 起,CDC 可以被用来构建"主备(primary-standby)"容灾拓扑:生产流量写入主集群,一个或多个备集群持续接收来自主集群的变更,同时对外提供读服务;当主集群不可用或需要维护时,可将服务流量切换到备集群,从而显著缩短业务中断时间。读完本文,你将理解 CDC 主备架构中的角色定义与行为约束、故障切换的两类手段及取舍(planned switchover 与 force failover),以及 CDC lag 对切换行为和数据安全的影响,为阅读后续的部署、切换与导入实践文档打下基础。

Milvus CDC 是什么

CDC 是一种数据同步范式:系统在捕获源端数据变化后,以流式方式将其应用到目标端。在 Milvus 中,CDC 的实现载体是一个独立的 CDC 节点(CDC node),它把"当前主集群"产生的 WAL(Write-Ahead Log)变更 持续转发给各备集群。因此,CDC 复制的对象本质上是消息流中带时序的 DML/DDL 操作序列,目标集群按序回放这些操作,从而保持与源集群一致。

值得强调的是,Milvus 的这套跨集群复制专门面向单一写入主(single-primary)的主备容灾模型设计,而不是多活(active-active)写入模型。复制的粒度是"物理通道(pchannel)"级别的复制任务——每个被复制的 DML 通道都对应一条复制链路,这正是"多通道并行复制、整体保序"的落地方式。

复制拓扑中的核心角色

一个典型的 Milvus CDC 复制拓扑包含以下组成部分(架构图):

组成 说明
主集群(Primary cluster) 复制操作的源端,同时接受读和写请求;是业务写入的唯一入口。
备集群(Standby cluster) 复制操作的目标端,持续接收主集群的变更;在保持备机身份的期间是只读的。
CDC 节点(CDC node) 转发 WAL 变更的 Milvus 组件,把当前主集群的变更投递到各备集群。凡是"可能在某次切换/故障转移后成为主集群"的集群,都应部署 CDC 节点。
复制拓扑(Replication topology) 已配置的"源到目标"关系,例如 cluster-a -> cluster-b,由跨集群拓扑定义描述。

Milvus CDC 主备复制架构图

从源码结构上看,CDC 节点是一个独立的服务模块,位于 internal/cdc/server.goCDCServer 内部持有并管理一个 controllerinternal/cdc/controller/controller.go),启动时拉起控制器,停止时释放。而复制拓扑的元数据(ReplicatePChannelMeta,即每个被复制 pchannel 的复制任务元信息)持久化在 etcd 中(internal/cdc/meta/replicate_meta.go),控制器正是通过监听 etcd 中该前缀下的元数据变化来驱动复制任务的创建与回收的。

一主一备拓扑(最常见)

Application writes
      |
      v
Primary cluster A  -- CDC replication -->  Standby cluster B

主集群 A 接受应用写入,通过 CDC 将变更持续同步给备集群 B。这是最常见、最容易理解与运维的部署形态,也是后续故障切换文档讨论的基础场景。

一主多备拓扑

Primary cluster A  -- CDC replication -->  Standby cluster B
                  \-- CDC replication -->  Standby cluster C

单主多备拓扑中,主集群 A 同时向多个备集群(B、C)复制数据。备集群之间没有复制关系、互不影响;当 A 故障时,你可以从多个备集群中挑选一个进行提升,提升后需相应调整复制方向。

主备集群的行为约束

角色 读(Reads) 写(Writes) 复制行为
主集群(Primary) 向各备集群发送变更
备集群(Standby) 接收来自主集群的复制变更

从上表可以看到两个关键设计:

  1. 备集群可以对外服务读流量。查询不会破坏一致性,因此容灾状态下读流量可以就近或持续打到备集群(前提是数据可见性预期需与主备间延迟对齐)。
  2. 备集群会拒绝直接写入请求。这是防止脑裂(split brain)、维持拓扑一致性的关键机制——如果备集群允许并发写入,两端数据会在各自的通道上产生无法调和的分叉。

主备角色的判定在 CDC 控制器中有明确体现:控制器从 etcd 读回复制通道元数据后,会将该通道的源通道名与当前集群 ID(paramtable 中的 CommonCfg.ClusterPrefix)比对,只有当前集群是某个复制任务的源时,才会为该通道创建 replicator(见 internal/cdc/controller/controller.gorecoverReplicatePChannelMeta 的实现)。这也解释了"每个可能成为主的集群都要部署 CDC 节点"的原因——角色切换后,原备集群需要立刻具备创建复制任务的能力。

由于复制方向会在主备切换时来回变化,etcd 中复制任务元数据的删除/重建需要处理异步时序冲突。源码中采用"携带 ModRevision 的乐观删除":RemoveReplicatePChannelWithRevision 通过 etcd 事务 Compare(ModRevision(key), "=", revision) 保证只有版本匹配时才删除,避免连续切换过程中出现 [Save, Delete, Save] 被乱序执行成 [Save, Save, Delete] 的竞态(详见 internal/cdc/meta/replicate_meta.go 中的注释)。

故障切换的两种选项

Milvus 提供两条把服务流量从主集群迁移到备集群的路径,核心差异在于是否允许丢数据是否要求旧主可达

操作 适用场景 数据丢失 预期恢复行为
计划切换(Planned switchover) 旧主仍可达;或需要做维护 RPO = 0 等待剩余复制数据全部就位后,再完成角色互换
强制故障转移(Force failover) 旧主完全不可用且短期内无法恢复 可能丢失 立即提升备集群为主,尽快恢复写入

选择原则:

  • 只要旧主还能响应,优先使用计划切换。计划切换等待剩余数据复制完成后再变更角色,保证 RPO = 0。
  • 只有当"恢复可用性"比"等待旧主恢复"更重要时,才使用强制故障转移。此时数据安全让位于可用性。

计划切换(Planned switchover)

计划切换的典型场景是把拓扑 cluster-a (primary) -> cluster-b (standby) 变为 cluster-b (primary) -> cluster-a (standby),全程不丢数据。核心操作是构造一份反向的完整替换配置cross_cluster_topology 中把源/目标对调,并把同一份配置依次应用到两端(先当前主、后当前备)。角色变更期间旧主降级为备并拒绝新写入,旧备等待剩余复制数据追平后自提升为主并恢复写入(完整流程与代码示例见 03-cdc-planned-switchover.md)。

值得注意:计划切换只是保证不丢数据,但操作耗时取决于尚待复制的数据量(即 CDC lag)。lag 越高,切换等待越久。

强制故障转移(Force failover)

强制故障转移把备集群提升为独立主集群(standalone primary):配置中只保留备集群本身、cross_cluster_topology 为空,并设置 force_promote=True,随后只向该备集群发送一次更新请求即可完成提升。该操作不等待旧主,未复制的数据会丢失。由于它通常是可用性优先的兜底手段,运维上需要特别注意:

  • 提升后立即把写流量全部切到新主;
  • 将旧主从写入端点、负载均衡、DNS 与自动化链路中彻底移除
  • 旧主恢复后不得再回连旧拓扑、不得接收应用写入——它可能含有从未复制到备机的"过期"数据,而新主在故障转移后可能已有新写入,二者已分叉(详见 04-cdc-force-failover.md)。

CDC Lag:复制延迟与切换风险窗口

CDC lag 定义为"已写入主集群、但尚未被应用到备集群"的数据量。它是判断复制健康度、指导切换决策的核心指标,直接影响故障转移行为:

  • 计划切换时:lag 越低,通常切换完成越快(新主只需追平少量剩余数据即可接管)。
  • 强制故障转移时:lag 就是旧主不可用时刻可能丢失的数据窗口——lag 越大,潜在丢失越多。

因此,官方建议持续监控 lag 并尽量压到最低。CDC lag 可能升高的情况包括:主集群写入速率高、集群间网络延迟或丢包增大、备集群过载、CDC 节点资源不足、运行大型 DDL 或导入任务等。

估算 CDC lag 的 PromQL

02-cdc-replication-quick-start.md 中给出了一个估算 lag 的 PromQL 查询(单位为秒):

clamp_min(
  max by (channel_name) (
    milvus_wal_last_confirmed_time_tick
    - timestamp(milvus_wal_last_confirmed_time_tick)
  )
  -
  min by (channel_name) (
    milvus_cdc_last_replicated_time_tick
    - timestamp(milvus_cdc_last_replicated_time_tick)
  ),
  0
)

语义拆解:

  • 对每个源通道(channel),milvus_wal_last_confirmed_time_tick 表示该通道上 WAL 最新确认的 timetick,milvus_cdc_last_replicated_time_tick 表示 CDC 最近一次完成复制投递的 timetick;
  • max by 取该通道最新写入点,min by 取该通道最慢的复制进度,二者之差即为滞后窗口,clamp_min(..., 0) 将负值归零;
  • 每个 gauge 上做 - timestamp(...) 是为了消除两条指标由不同抓取目标采样而产生的相位差——若直接对两个裸 gauge 做差,即使系统空闲,结果也会以约一个抓取间隔的幅度振荡。

如果 Prometheus 同时抓取多个 Milvus 集群,请附加与部署匹配的标签过滤(如 namespaceapp_kubernetes_io_instance),避免把不同集群的指标混在一起。

从源码侧可以验证该指标的维护链路:复制流在推进时会更新 CDCLastReplicatedTimeTick 指标,其 LabelValues 携带源通道名与目标通道名(见 internal/cdc/replication/replicatestream/metrics.goUpdateLastReplicatedTimeTick/InitLastReplicatedTimeTick 的实现);当复制任务结束或方向变更时,通过 DeleteLastReplicatedTimeTick 清理对应的时间序列,避免指标泄漏。

复制期间的数据导入

在主备复制拓扑持续运行期间,仍然可以向集群导入数据,但必须使用两阶段提交(two-phase commit,2PC)导入模式:导入在源端先进入 Uncommitted(数据不可见)状态,提交动作再作为一个"有序栅栏(ordered fence)"复制到备端,使主备两端在同一个逻辑点位让数据可见。普通的一阶段 auto_commit=true 导入在复制集群中会被拒绝。配置项 dataCoord.import.enableInReplicatingCluster 需要同时在主备两端开启,该参数支持热刷新、无需完整重启。完整步骤与错误对照表见 05-cdc-2pc-import.md

从总览到实操:后续阅读路径

本文所属的 CDC 用户文档是一套循序渐进的系列,承接总览内容后可依次阅读:

文档 内容
02-cdc-replication-quick-start.md 用 Milvus Operator 部署 source/target 两个 standalone 集群,通过 update_replicate_configuration 下发复制配置并验证同步;含 CDC lag 的 PromQL 估算示例
03-cdc-planned-switchover.md 无数据丢失的角色互换流程(RPO = 0),含反向拓扑构造、应用顺序与切换回退
04-cdc-force-failover.md force_promote=True 的兜底提升流程、数据丢失风险与防脑裂要点
05-cdc-2pc-import.md 复制模式下的两阶段提交批量导入实操

仓库内另有若干与 CDC 相关的设计/专题文档(如 get_replicate_configurationforce_promote_failoverdata_salvage_for_force_failover、per-cluster mTLS 用户指南等,位于 docs/design-docs/design_docs/cdc/),需要了解配置读取、强制提升与数据抢救的底层设计时可进一步查阅。

常见问题(FAQ)

备集群可以对外提供查询吗?

可以。备集群在保持备机身份期间能够服务读流量,但不能接受写入,只有被提升为主之后才能写入。

CDC 支持双活(active-active)写入吗?

不支持。CDC 复制专为**单主(single-primary)**拓扑设计。如果同时对多个集群写入,会造成脑裂与数据分叉,破坏一致性。

计划切换会丢数据吗?

不会。计划切换会等待剩余数据完成复制后,备集群才升为主,因此 RPO = 0。

强制故障转移会丢数据吗?

可能丢,但不是必然。若旧主故障前所有写入都已完成复制则无丢失;若存在 lag,则未复制的部分可能丢失。

强制故障转移最多可能丢多少数据?

潜在丢失量以旧主不可用时刻的 CDC lag 为上界——lag 越大,风险窗口越大。

复制拓扑运行期间还能导入数据吗?

可以。复制集群中的导入必须以两阶段提交(2PC)模式运行(auto_commit=false),具体见 Bulk Import in CDC Replication Mode(05-cdc-2pc-import.md)。普通自动提交的一阶段导入在复制集群中会被拒绝。

小结

  • Milvus CDC 从 v2.6 起为跨集群主备容灾提供官方能力,核心组件是独立部署的 CDC 节点与 etcd 中持久化的每通道复制元数据。
  • 复制拓扑严格单主:主集群读写、备集群只读并拒绝直接写入,从而从机制上杜绝脑裂。
  • 服务迁移有两条路径:计划切换(RPO = 0,要求旧主可达)与强制故障转移(可用性优先,丢失量以 CDC lag 为界),应按业务对可用性与数据安全的需求取舍。
  • CDC lag 是贯穿部署、监控与切换决策的关键指标,可使用 WAL 与 CDC 两套 timetick 指标的差值进行 PromQL 估算并持续告警。
  • 结合仓库源码(internal/cdc/server.gointernal/cdc/controller/controller.gointernal/cdc/meta/replicate_meta.go)可进一步理解 CDC 节点如何通过 etcd 元数据驱动复制任务、如何在角色切换时安全回收任务,这些机制共同保证了主备方向的可靠倒换。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391