GaussDB 高可用演进:从流复制到 DCF 分布式共识

GaussDB 高可用演进:从流复制到 DCF 分布式共识

  在上一篇中,我们介绍了基于 PostgreSQL + Patroni + etcd的高可用部署方式,其特点包括:

  • Patroni 负责 PostgreSQL 实例的生命周期管理(启动、停止、角色切换、故障转移等);
  • ETCD 作为外部分布式一致性存储,承担 Leader 选举集群元数据 的职责;
  • 集群依赖 ETCDRaft 协议** 来保证配置信息的一致性;
  • 数据节点本身并不参与共识,仅作为状态跟随者。

  这种架构的优势是成熟、生态完善,但代价是 多引入了一层外部依赖(etcd) ,部署与运维复杂度也随之上升。今天我们来探索一下 GaussDB 的复制演进。

1. 传统流复制时代:CM 仲裁与流复制的协同

   当 enable_dcf = off 时,GaussDB 集中式集群为传统的"物理流复制 + 控制面仲裁"模式:

  • 数据平面 :主库 walsender 线程通过 TCP 将 WAL(XLOG)字节流单向推送至备库 walreceiver 线程。尽管包含了内核级优化,但底层仍为点对点日志流复制,无分布式共识算法参与
  • 控制平面:高可用选主与仲裁由 CM 接管。元数据存储与仲裁依赖可以选用 ETCD;在无 ETCD 部署下,亦可通过内置的 DCC 组件完成状态协同与选主。
sql 复制代码
-- 流复制总开关
gaussdb=> show enable_stream_replication;
 enable_stream_replication 
---------------------------
 on
(1 row)

-- Quorum 仲裁模式,ANY 1 在一主两备架构下,等同于多数派
gaussdb=>  show synchronous_standby_names;
 synchronous_standby_names 
---------------------------
 ANY 1(dn_6002,dn_6003)
(1 row)

-- 必须等待备库的 walreceiver 线程将该日志成功写入(Flush)磁盘
gaussdb=> show synchronous_commit;
 synchronous_commit 
--------------------
 on
(1 row)

2. DCF 模式

2.1 概念

  前面多次提到了 DCF(Distributed Consensus Framework) ,那么到底什么是 DCF?GaussDB 又为什么需要自研出这一套共识框架?

   因为基于多数派共识协议(如 Paxos、Raft)实现数据强一致与自愈,已成为现代分布式数据库的主流趋势。当前,主流分布式数据库存储引擎与共识协议对照如下:

数据库产品 存储引擎 副本共识/同步协议
OceanBase LSM-Tree Multi-Paxos, 自研 Paxos 变体
TiDB LSM-Tree Multi-Raft
TDSQL for MySQL B+Tree Raft + MARS 强同步
TDSQL Boundless LSM-Tree Raft
GoldenDB B+Tree Raft 增强型日志复制

  相比之下,GaussDB 走了一条更为灵活的技术路径:它并未一上来就彻底抛弃 PostgreSQL 的继承演进,而是保留了经典的物理流复制 ,并在此基础上引入了自研的分布式共识框架 ------ DCF(基于 Paxos 协议),实现了两套高可用模式的平滑切换。

  • enable_dcf = off(非 DCF 模式) :采用传统物理流复制。配合 synchronous_standby_names = 'ANY N' 配置实现 Quorum 多数派同步;集群选主交由上层 CM 组件,依据 Term 与 LSN 位置进行仲裁,不经过 Paxos 协议。
  • enable_dcf = on(DCF 模式):DCF 动态库直接挂载至 DN 进程。底层通过 Paxos 协议实现 DN 内核级的自选主与 XLOG 多副本复制;此时 CM 不再干预 DN 选主,仅负责状态采集与假死检测。

两种模式的技术细节对比如下:

对比维度 DCF 模式 (enable_dcf = on) 非 DCF 模式 (enable_dcf = off)
副本共识协议 **DCF (Paxos)**基于自研 Paxos 协议进行多数派复制。 物理流复制基于 PG 流复制 + ANY N (Quorum)。
存储引擎支持 ASTore / UStore / CStore ASTore / UStore / CStore
选主与仲裁 DN 内置 Paxos 仲裁内核自主选主;CM 仅负责探测。 CM 外部仲裁由 CM 组件根据 Term 与 LSN 外部选主。

2.2 实操验证

开启 DCF 后,可以通过以下命令检查参数:

sql 复制代码
gaussdb=> show enable_dcf;   
 enable_dcf 
------------
 on
(1 row)

gaussdb=> show dcf_run_mode;
 dcf_run_mode 
--------------
 1
(1 row)

[omm@gauss1 cm]$ cm_ctl list --param --server | grep dn_arbitrate_mode
dn_arbitrate_mode = quorum
dn_arbitrate_mode = quorum
dn_arbitrate_mode = quorum

gaussdb=> SELECT application_name, client_addr, state, sync_state,
pg_xlog_location_diff(sender_sent_location, receiver_replay_location) AS lag_bytes
FROM pg_stat_replication;gaussdb-> gaussdb-> 
 application_name | client_addr | state | sync_state | lag_bytes 
------------------+-------------+-------+------------+-----------
(0 rows)

   可以看到,开启 DCF 之后,传统的流复制视图(如 pg_stat_replication)可能会变为空,这是因为流复制的底层传输和同步机制已经被 DCF 的 Paxos 框架所接管了

sql 复制代码
-- 开启 DCF 后,通过如下命令查看延迟
[omm@gauss1 dn_6001]$ gs_ctl query -D /data/cluster/data/dn/dn_6001
[2026-09-10 15:06:01.797][261164][][gs_ctl]: gs_ctl query ,datadir is /data/cluster/data/dn/dn_6001 

 HA state:           
	local_role                     : Primary
	static_connections             : 2
	db_state                       : Normal
	detail_information             : Normal

 Paxos replication info:
	paxos_write_location           : 0/D8A72D0
	paxos_commit_location          : 0/D8A72D0
	local_write_location           : 0/D8A72D0
	local_flush_location           : 0/D8A72D0
	local_replay_location          : 0/D8A72D0
	dcf_replication_info           : {"stream_id":1,"local_node_id":1,"role":"LEADER","term":407,"run_mode":1,"work_mode":0,"stg_mode":1,"is_fixed_leader":0,"hb_interval":1000,"elc_timeout":3000,"auto_elc_pri_en":1,"elc_switch_thd":0,"group":0,"priority":212705128,"leader_group":0,"is_in_major":1,"applied_index":8173,"commit_index":8173,"first_index":1,"last_index":227177168,"last_term":407,"dcf_last_index":8173,"dcf_last_disk":8173,"is_arbitrating":0,"cluster_min_apply_idx":8173,"cluster_mode":"primary_cluster","leader_id":1,"leader_ip":"192.168.182.138","leader_port":30106,"nodes":[{"node_id":1,"ip":"192.168.182.138","port":30106,"role":"LEADER","next_index":8174,"match_index":8173,"apply_index":8173},{"node_id":2,"ip":"192.168.182.139","port":30106,"role":"FOLLOWER","next_index":8174,"match_index":8173,"apply_index":8173},{"node_id":3,"ip":"192.168.182.140","port":30106,"role":"LOGGER","next_index":8174,"match_index":8173,"apply_index":8173}]}
	
	
gs_ctl query -D /data/cluster/data/dn/$(ls /data/cluster/data/dn/ | head -n 1) 2>/dev/null | tr -cd '\11\12\40-\176' | python3 -c "import sys, json; raw = sys.stdin.read(); kw = 'dcf_replication_info'; idx = raw.find(kw); parts = raw[raw.find('{', idx):raw.rfind('}')+1]; data = json.loads(parts) if parts else None; l_node = next((n for n in data['nodes'] if n['role']=='LEADER'), None) if data else None; l_idx = l_node.get('apply_index', l_node.get('match_index', 0)) if l_node else 'N/A'; print('='*70 + '\n                GaussDB DCF Sync Monitor\n' + '='*70 + f'\n  Current Role: {data.get(\"role\",\"UNKNOWN\") if data else \"UNKNOWN\"}\n  LEADER Index: {l_idx}\n' + '-'*70 + '\n Node ID |      IP:Port         |    Role     |  Index   |   Lag Count\n' + '-'*70) if data else print('[Error] Cannot parse DCF info from this node'); [print(f'  Node {n[\"node_id\"]:<2} | {n[\"ip\"]+\":\"+str(n[\"port\"]):<20} | {n[\"role\"]:<11} | {n.get(\"apply_index\", n.get(\"match_index\", 0)):<8} | ' + (f'Lags {l_idx - n.get(\"apply_index\", n.get(\"match_index\", 0))}' if l_idx!='N/A' and l_idx - n.get('apply_index', n.get('match_index', 0))>0 else ('-' if n['role']=='LEADER' else 'Healthy')) ) for n in data['nodes']] if data else None; print('='*70)"

======================================================================
                GaussDB DCF Sync Monitor
======================================================================
  Current Role: LEADER
  LEADER Index: 11012
----------------------------------------------------------------------
 Node ID |      IP:Port         |    Role     |  Index   |   Lag Count
----------------------------------------------------------------------
  Node 1  | 192.168.182.138:30106 | LEADER      | 11012    | -
  Node 2  | 192.168.182.139:30106 | FOLLOWER    | 11012    | Healthy
  Node 3  | 192.168.182.140:30106 | LOGGER      | 11012    | Healthy
======================================================================

关注以下几个位置字段:

nodes 列表里会列出所有 3 个节点(Leader、Follower、Logger)的实时 Index:

  • match_index :代表该备节点已成功接收并持久化**的 Paxos 日志位置。
  • apply_index :代表该备节点已实际回放/应用到数据库**的日志位置。
  • paxos_write_location (Paxos 写入点)
  • paxos_commit_location (Paxos 多数派提交点)
  • local_flush_location (本地刷新点)
  • local_replay_location (本地回放点)

2.3 底层接管逻辑

   DCF 作为一个深度嵌入到数据库内核的动态库(Plugin)。一旦开启,数据库内核会绕过原生的 Sender/Receiver 通道。WAL 日志的传输、确认以及网络心跳,全部由 DCF 内部的线程池和通信模块(基于 Paxos 协议)来独立完成。

  1. DN 事务写 XLOG → 交给 DCF 写接口
    • DN 在处理事务时,依然负责生成标准格式的 XLOG(WAL 日志),但日志的持久化确认与分发权交给了 DCF 引擎。
    • 事务在等待 Commit 成功时,不再等待本地或传统流复制的 ACK,而是挂起在 DCF 的 Consensus Log Index 屏障上,直到 DCF 返回"多数派提交"通知。
  2. DCF 用自带通信模块发到多数派;
    • DCF 内部拥有自研的、轻量级的通信线程池(与传统的 WalSender/WalReceiver 进程隔离)。
    • DCF 拿到 XLOG 后,在自己的网络栈中将其作为 Paxos Proposal(提案)广播给集群中的 Follower/Learner。只要收到过半数节点的持久化响应(Quorum ACK),DCF 就会通知 DN 引擎"该日志已安全提交",DN 随即回复客户端事务成功。
  3. 传统同步机制退化与 CM 仲裁的"放权"
    • 传统模式(dn_arbitrate_mode = quorum / cms:CM 负责通过探针轮询 DN 状态,由 CM 外部决策谁来做 Primary、谁来 Promote,极易因网络抖动造成误判或脑裂。
    • DCF 模式(dn_arbitrate_mode = paxos :CM 的强行脑裂仲裁逻辑被完全关闭。选主和状态防脑裂的绝对控制权交给了内核层 DCF 的 Paxos Term/Lease 机制。CM 仅作为集群外部的静态状态监控与运维工具,不再主动"强行插手"DN 的升主降备过程。

2.4 仲裁与运行模式矩阵

GaussDB 参数体系中,CM 的仲裁模式与 DN 的 DCF 参数存在以下典型组合:

dn_arbitrate_mode enable_dcf dcf_run_mode 实际模式 谁选主
quorum(默认) off --- 纯 Quorum 模式​ CM 仲裁,DN 走流复制
paxos on​ 0 (auto) DCF 自动选主 DCF 自选主,CM 只做假死检测
quorum on​ 1 (manual) DCF 手动选主(DCF 搬运日志,CM 仲裁) CM 仲裁,DCF 只搬日志
share_disk off --- 共享存储模式 存储层 / CM 配合进行控制与复制

注意:

  • dn_arbitrate_mode 是 CM 的参数;
  • enable_dcf 和 dcf_run_mode 是 DN 的参数;

3. 流复制 VS DCF

3.1 核心底层协议与算法

  • 传统流复制 :基于经典的主备架构。主库的 walsender 线程通过原生 TCP 协议,将 WAL 字节流单向、直接地推送至备库的 walreceiver 线程。其内核层面不涉及复杂的分布式共识与投票算法。
  • DCF 模式 :基于 Paxos 分布式共识协议。GaussDB 内核将数据同步的控制权下沉(旁路)至底层的 C++ Paxos 共识引擎(存储路径为 $DATAPATH/dcf_data)。数据不再是简单的点对点单向推送,而是通过 Paxos Log 的形式在集群内进行多数派表决与确认。

3.2 多数派状态确认

  • 传统流复制 :依赖 synchronous_commitsynchronous_standby_names 参数组合。

    sql 复制代码
    gaussdb=>  show synchronous_standby_names;
     synchronous_standby_names 
    ---------------------------
     ANY 1(dn_6002,dn_6003)
    (1 row)
    
    gaussdb=> show synchronous_commit;
     synchronous_commit 
    --------------------
     on
    (1 row)
    • 在该配置下,主库收到写请求,需要强行等待 6002 或 6003 中至少一个物理节点回复"已写入磁盘"的 ACK 响应,事务才能在主库提交。如果遇到网络抖动,主库的写入性能会直接受到拖累。
  • DCF 模式 :遵循 Paxos 多数派协议

    • 集群内发送一条事务,只要 **超过半数确认接收到了 Paxos Log(match_index 对齐),该事务就认为达成了共识(Consensus)。
    • 即使其中某个备库网络卡顿或直接宕机,只要剩下两个节点是通的,主库的写入不会受到阻塞。

3.3 高可用切换与自愈能力

  • 传统流复制内核自身没有盲目的、绝对安全的自动选主能力。

    • 传统的主备高可用深度依赖外部的集群管理组件(CM)。一旦主库挂了,CM 需要去仲裁谁当新主。如果 CM 自身网络发生分区,极易引发"脑裂"。
  • DCF 模式具备绝对安全的、内核级自动选主能力(无需 CM 也能自愈)。

    • Paxos 协议天然自带严格的 Term(任期号) 和投票机制。一旦 Leader 宕机,Follower 节点会自动发起选举,仅有获得过半数(Quorum)选票且日志最新的节点才能晋升为新 Leader。由于数学逻辑上不可能同时产生两个过半数集群,因此从根源上杜绝了脑裂的可能性。

3.4 节点角色和磁盘开销的弹性

  • 传统流复制 :每个加入流复制的备节点,都必须是完整的数据库实体
    • 备库必须既维护完整的物理数据文件(Data File),又接收并重做 WAL 日志。节点数量增加意味着磁盘与计算开销成倍翻番。
  • DCF 模式 :支持更丰富的轻量化节点角色。
    • 在 DCF 模式下,三节点集中式集群中 Node3 可以配置为 LOGGER(日志节点/仲裁节点),只参与 Paxos 投票和记录共识日志,不回放数据实体、不存物理数据页

这不仅极大节省了第三台机器的磁盘和内存开销,同时又完美凑齐了多数派所需的奇数节点。

4. 总结

  从传统流复制向 DCF 模式的演进,是 GaussDB 走向真正的强一致性分布式架构的里程碑。从传统的物理流复制跨越到 DCF 模式,不仅是日志传输技术的升级,更是一场高可用架构的升维:

  • 控制维度的升维:从"外部强加"到"内核自治";
  • 数据维度的升维:从"点对点流转"到"多数派共识";
  • 资源维度的升维:从"重型镜像"到"弹性角色"。

   DCF 模式 代表了未来的方向,通过内置 Paxos 实现了真正的分布式共识,去 ETCD 化,使数据库自身成为一个更自治、更健壮的分布式系统。

  这里是《实战派 K8S&DB》,更多干货敬请关注公众号。如果本文对你有所帮助,欢迎点赞、推荐和转发,也欢迎关注后续文章,一起考证、一起学习 GaussDB 分布式数据库。

相关推荐
写后端的胖头鱼1 小时前
【高频面试题】分布式锁在项目中的应用
java·分布式·后端·分布式锁·高频面试题
clz13145212 小时前
Kafka 日消 10 亿场景:用 ConcurrentLinkedQueue 实现高性能批量消费缓冲
分布式·kafka·linq
隐擎fox13 小时前
高性能网络爬虫架构设计:基于 Python 的长连接复用与分布式会话池调度实践
分布式·python·网络协议·tcp/ip·高并发·网络爬虫、
闲云自留地19 小时前
云平台存储管理员:Cinder 创建挂载卷 + Swift 分布式存储原理
分布式·wpf·swift
橙子圆1231 天前
RabbitMQ知识1
分布式·rabbitmq
clz13145211 天前
风险特征系统 EPC 子系统:基于 Kafka 数据源与数据集配置的 Flink 指标清洗加工
分布式·flink·kafka
海上小飞龙1 天前
分布式和微服务,一次讲清
分布式·微服务·架构
(Charon)1 天前
【Kafka】消息队列学习(二):Topic、Partition与Offset,消息到底存在哪里
分布式·学习·kafka