GaussDB 高可用演进:从流复制到 DCF 分布式共识
在上一篇中,我们介绍了基于 PostgreSQL + Patroni + etcd的高可用部署方式,其特点包括:
Patroni负责PostgreSQL实例的生命周期管理(启动、停止、角色切换、故障转移等);ETCD作为外部分布式一致性存储,承担 Leader 选举 与 集群元数据 的职责;- 集群依赖
ETCD的Raft协议** 来保证配置信息的一致性; - 数据节点本身并不参与共识,仅作为状态跟随者。
这种架构的优势是成熟、生态完善,但代价是 多引入了一层外部依赖(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 协议)来独立完成。
- DN 事务写 XLOG → 交给 DCF 写接口
- DN 在处理事务时,依然负责生成标准格式的 XLOG(WAL 日志),但日志的持久化确认与分发权交给了 DCF 引擎。
- 事务在等待
Commit成功时,不再等待本地或传统流复制的 ACK,而是挂起在 DCF 的 Consensus Log Index 屏障上,直到 DCF 返回"多数派提交"通知。
- DCF 用自带通信模块发到多数派;
- DCF 内部拥有自研的、轻量级的通信线程池(与传统的
WalSender/WalReceiver进程隔离)。 - DCF 拿到 XLOG 后,在自己的网络栈中将其作为 Paxos Proposal(提案)广播给集群中的 Follower/Learner。只要收到过半数节点的持久化响应(Quorum ACK),DCF 就会通知 DN 引擎"该日志已安全提交",DN 随即回复客户端事务成功。
- DCF 内部拥有自研的、轻量级的通信线程池(与传统的
- 传统同步机制退化与 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_commit与synchronous_standby_names参数组合。sqlgaussdb=> 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)。 - 即使其中某个备库网络卡顿或直接宕机,只要剩下两个节点是通的,主库的写入不会受到阻塞。
- 集群内发送一条事务,只要 **超过半数确认接收到了 Paxos Log(
3.3 高可用切换与自愈能力
-
传统流复制 :内核自身没有盲目的、绝对安全的自动选主能力。
- 传统的主备高可用深度依赖外部的集群管理组件(CM)。一旦主库挂了,CM 需要去仲裁谁当新主。如果 CM 自身网络发生分区,极易引发"脑裂"。
-
DCF 模式 :具备绝对安全的、内核级自动选主能力(无需 CM 也能自愈)。
- Paxos 协议天然自带严格的 Term(任期号) 和投票机制。一旦
Leader宕机,Follower节点会自动发起选举,仅有获得过半数(Quorum)选票且日志最新的节点才能晋升为新 Leader。由于数学逻辑上不可能同时产生两个过半数集群,因此从根源上杜绝了脑裂的可能性。
- Paxos 协议天然自带严格的 Term(任期号) 和投票机制。一旦
3.4 节点角色和磁盘开销的弹性
- 传统流复制 :每个加入流复制的备节点,都必须是完整的数据库实体 。
- 备库必须既维护完整的物理数据文件(Data File),又接收并重做 WAL 日志。节点数量增加意味着磁盘与计算开销成倍翻番。
- DCF 模式 :支持更丰富的轻量化节点角色。
- 在 DCF 模式下,三节点集中式集群中 Node3 可以配置为
LOGGER(日志节点/仲裁节点),只参与Paxos投票和记录共识日志,不回放数据实体、不存物理数据页。
- 在 DCF 模式下,三节点集中式集群中 Node3 可以配置为
这不仅极大节省了第三台机器的磁盘和内存开销,同时又完美凑齐了多数派所需的奇数节点。
4. 总结
从传统流复制向 DCF 模式的演进,是 GaussDB 走向真正的强一致性分布式架构的里程碑。从传统的物理流复制跨越到 DCF 模式,不仅是日志传输技术的升级,更是一场高可用架构的升维:
- 控制维度的升维:从"外部强加"到"内核自治";
- 数据维度的升维:从"点对点流转"到"多数派共识";
- 资源维度的升维:从"重型镜像"到"弹性角色"。
DCF 模式 代表了未来的方向,通过内置 Paxos 实现了真正的分布式共识,去 ETCD 化,使数据库自身成为一个更自治、更健壮的分布式系统。
这里是《实战派 K8S&DB》,更多干货敬请关注公众号。如果本文对你有所帮助,欢迎点赞、推荐和转发,也欢迎关注后续文章,一起考证、一起学习 GaussDB 分布式数据库。