双副本集群高可用
概述
KaiwuDB 双副本集群提供两种高可用部署形态,适配不同服务器资源条件(以三台服务器为例): 部署形态一:资源不均衡场景(2 数据节点 + 1 仲裁节点) 当集群内服务器配置差异较大,单台节点资源显著弱于其他节点时,推荐采用仲裁节点架构。仲裁节点不承载时序业务数据,仅参与 Raft 共识投票,保障集群选主与故障判定。 部署形态二:资源均衡场景(2 数据副本 + 仲裁副本,无独立仲裁节点) 当各服务器硬件配置均衡,希望在双副本降低存储冗余的同时最大化利用整机资源,可采用内嵌仲裁副本,不单独部署独立仲裁节点;仲裁副本与数据副本均衡打散分布于各节点,进一步提升硬件资源利用率。
以上两种部署形态,可根据实际业务情况进行切换。
说明
- 双副本策略仅对时序数据生效,关系数据和元数据仍采用三副本。
- RPO 可通过集群参数
ts.arbiter.rpo_max_duration配置,默认不限制。
基础概念
| 概念 | 描述 |
|---|---|
| 数据副本(Data Replica) | 存储实际时序业务数据的副本,正常参与 Raft 共识和选举,可当选 Leader 和 Leaseholder。双副本方案中每个数据分片有 2 个数据副本,分布在数据节点上。 |
| 仲裁副本(Arbitration Replica) | 不存储时序业务数据的副本,参与 Raft 共识和选举,可当选 Leader 但优先级较低,不可成为 Leaseholder。正常情况下 Leader 由数据副本担任,仅在数据节点故障时仲裁副本才可能当选 Leader。 |
| 仲裁节点(Arbiter Node) | 承载仲裁副本的节点,通常为集群中配置较低或异构的机器。仲裁节点仍需存储关系数据和元数据,并参与集群心跳。 |
| 数据节点(Data Node) | 承载数据副本的节点,负责时序数据的实际存储与读写服务。 |
使用限制
双副本集群存在以下限制:
- 集群中至少有 2 个数据节点和 1 个仲裁节点【部署形态一】。
- 每个数据分片的副本构成固定为 2 个数据副本 + 1 个仲裁副本,不允许通过
CONFIGURE ZONE USING num_replicas修改副本数量。 - 双副本策略只能通过
ALTER RANGE ts CONFIGURE ZONE USING arbiter = true或ALTER DATABASE ... CONFIGURE ZONE USING arbiter = true启用,不支持表级别设置,不支持对已有表从三副本切换为双副本。 - 启用双副本后,不支持切换回三副本模式。
部署配置
前提条件
- 已准备至少三台服务器。
- 各服务器之间网络互通,且时钟偏差小于 500 ms。
步骤
- 在个服务器上启动服务
部署形态一
以三台服务器为例,部署 2 数据节点 + 1 仲裁节点
(1)在数据节点服务器上分别执行以下命令,启动数据节点:
# 数据节点 A
./kwbase start \
--insecure \
--store=/data \
--listen-addr=<node_a_address>:26257 \
--advertise-addr=<node_a_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<node_a_address>:8080 \
--join=<node_a_address>,<node_b_address>,<node_c_address> \
--background
# 数据节点 B
./kwbase start \
--insecure \
--store=/data \
--listen-addr=<node_b_address>:26257 \
--advertise-addr=<node_b_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<node_b_address>:8080 \
--join=<node_a_address>,<node_b_address>,<node_c_address> \
--background
(2)在仲裁节点服务器上执行以下命令,启动仲裁节点。注意必须加上 --arbiter 参数:
# 仲裁节点 C
./kwbase start \
--insecure \
--store=/data \
--arbiter \
--listen-addr=<node_c_address>:26257 \
--advertise-addr=<node_c_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<node_c_address>:8080 \
--join=<node_a_address>,<node_b_address>,<node_c_address> \
--background
部署形态二
以三台服务器为例,在各台服务器上执行启动命令
# 节点 A
./kwbase start \
--insecure \
--store=/data1 \
--listen-addr=<node_a_address>:26257 \
--advertise-addr=<node_a_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<node_a_address>:8080 \
--join=<node_a_address>,<node_b_address>,<node_c_address> \
--background
# 节点 B
./kwbase start \
--insecure \
--store=/data \
--listen-addr=<node_b_address>:26257 \
--advertise-addr=<node_b_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<node_b_address>:8080 \
--join=<node_a_address>,<node_b_address>,<node_c_address> \
--background
# 节点 C
./kwbase start \
--insecure \
--store=/data \
--listen-addr=<node_b_address>:26257 \
--advertise-addr=<node_c_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<node_c_address>:8080 \
--join=<node_a_address>,<node_b_address>,<node_c_address> \
--background
- 在任意节点上执行集群初始化命令:
./kwbase init --insecure --host=<node_a_address>:26257
连接到集群后,执行以下语句启用双副本策略:
-- 对全部时序数据生效,执行时集群中不能存在任何时序表 ALTER RANGE ts CONFIGURE ZONE USING arbiter = true; -- 对全部时序数据生效,执行时目标数据库中不能存在时序表,且保证数据不丢失 ALTER RANGE ts CONFIGURE ZONE USING arbiter = true, arbiter_with_log = true; -- 仅对指定数据库生效,执行时目标数据库中不能存在时序表 ALTER DATABASE <db_name> CONFIGURE ZONE USING arbiter = true; -- 对指定数据库生效,执行时目标数据库中不能存在时序表,且保证数据不丢失 ALTER DATABASE <db_name> CONFIGURE ZONE USING arbiter = true, arbiter_with_log = true;系统随后会将时序数据分片自动分配为 2 个数据副本 + 1 个仲裁副本,根据部署形态进行仲裁副本和数据副本的迁移。如果是存在仲裁节点的部署形态一,仲裁副本会迁移至仲裁节点,数据副本迁移至数据节点,并将 Leaseholder 迁移至数据节点。
说明
- 初始化完成后副本补足和 Leaseholder 迁移需要一定时间,期间首次建表的响应时间会略长于标准三副本集群。
- 设置
arbiter_with_log时的前提条件是arbiter = true,否则会报错could not validate zone config:must set arbiter to true before set arbiter_with_log。并且不支持从true修改为false。 - 开启
arbiter_with_log后,仲裁节点上会存储 raft log ,会比不开启时占用相对多的磁盘空间。 - 启用后不支持切换回三副本模式。
扩缩容
双副本集群支持通过扩缩容处理节点故障、调整集群规模,或是调整部署形态。每个数据分片的副本构成标准始终为 2 个数据副本 + 1 个仲裁副本,允许暂时缺少一个数据副本或仲裁副本,但不允许数据副本数量超过 2 个,也不允许仲裁副本数量超过 1 个。
注意:缩容之前,必须保证缩容之后集群规模 >=3,且数据副本不少于2,否则缩容失败。
加入新节点
当节点故障无法恢复,或需要主动扩容时,可加入新节点:
加入新数据节点
./kwbase start \
--insecure \
--store=/data_new \
--listen-addr=<new_node_address>:26257 \
--advertise-addr=<new_node_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<new_node_address>:8080 \
--join=<existing_node_address>:26257 \
--background
加入新仲裁节点
./kwbase start \
--insecure \
--store=/data_new \
--arbiter \
--listen-addr=<new_node_address>:26257 \
--advertise-addr=<new_node_address>:26257 \
--brpc-addr=:27257 \
--http-addr=<new_node_address>:8080 \
--join=<existing_node_address>:26257 \
--background
新节点加入后,系统可能触发副本自动均衡,将部分副本迁移至新节点。
退役节点
加入新节点后,可执行退役操作移除故障节点:
./kwbase node decommission <node_id> --insecure --host=<address>:26257
退役操作会将故障节点上的副本迁移至新节点,等待命令执行完成后,所有副本已在新节点上补全。
注意
- 退役数据节点时,集群规模要求至少为 4,且至少有 3 个数据节点(包含待退役节点),否则退役将失败。
也可以不立即执行退役,等待故障节点被系统判定为死亡后,系统会自动在其他节点上补足副本。
两种部署形态切换
无仲裁节点双副本集群切换至有仲裁节点双副本集群
有仲裁节点双副本集群切换至无仲裁节点双副本集群
高可用机制
双副本方案通过 Raft 投票机制实现高可用,可容忍任意一个节点故障。
单节点故障
数据节点故障
当一个数据节点不可用时,剩余的 1 个数据副本与仲裁副本组成 2/3 多数,达成 Raft 共识,由剩余数据节点继续对外提供读写服务。节点恢复后,自动重新加入集群并与 Leader 完成数据同步,读写服务不受影响。
仲裁节点故障
当仲裁节点不可用时,2 个数据副本自身即可达成 2/2 多数,集群读写服务不受影响。节点恢复后,自动重新加入集群,恢复投票能力。
多节点故障
当任意两个节点同时故障时(包括两个数据节点同时故障,或一个数据节点与仲裁节点同时故障),集群无法达成多数共识,写入操作将失败。待任意一个节点恢复后,集群恢复服务,读写恢复正常。
当所有节点故障时,写入操作将失败。待节点全部恢复后,集群自动完成选举并恢复读写服务。
数据一致性保证
双副本方案采用弱一致性保证:数据写入 Leader 副本后即视为成功,Follower 数据副本可能存在短暂落后。可通过集群参数 ts.arbiter.rpo_max_duration 配置数据同步周期,控制最大数据丢失量。详细信息参见配置 RPO。
数据完整性
开启 Raft log后,数据节点离线后,将使用仲裁副本上包含数据的 Raft log 补齐 follow 数据副本,保证数据不丢失。在数据补齐期间,无 leaseholder 可用,无法进行读写。 仲裁副本上存储足够的供数据副本 follow 数据补齐的 Raft log,因此需要更多的磁盘空间、IO资源。
相关操作
配置 RPO
ts.arbiter.rpo_max_duration 用于控制双副本模式下数据节点间数据同步的时间周期,即最大允许丢失的数据时间范围。仅在启用双副本模式时生效。
SET CLUSTER SETTING ts.arbiter.rpo_max_duration = '60s';
| 取值 | 说明 |
|---|---|
-1(默认值) | 不进行 RPO 控制,数据节点之间无需进行周期性数据同步。 |
大于 1s 的时间值 | 系统按设定周期在数据节点之间同步数据,强制同步期间阻塞写入。如果有数据节点不可用,写入将被阻塞,直至数据节点恢复或新数据节点加入且故障节点退役后,写入才会继续执行。 |
注意
开启 RPO 控制后,如果有数据节点故障,写入将持续阻塞。仲裁节点故障不影响数据写入。
查看节点状态
执行以下命令查看集群节点状态,输出中 node_mode 列显示各节点角色:
./kwbase node status --insecure --host=<address>:26257
输出示例:
id | address | sql_address | build | started_at | updated_at | locality | is_available | is_live | node_mode
-----+-------------------+-------------------+---------+-----------------------------+-----------------------------+---------------+--------------+---------+-----------
1 | node_a:26257 | node_a:26257 | V3.2.0 | 2026-03-02 09:24:56.827856 | 2026-03-02 09:25:01.343206 | region=NODE1 | true | true | normal
2 | node_b:26257 | node_b:26257 | V3.2.0 | 2026-03-02 09:24:58.012345 | 2026-03-02 09:25:02.234567 | region=NODE2 | true | true | normal
3 | node_c:26257 | node_c:26257 | V3.2.0 | 2026-03-02 09:25:00.543210 | 2026-03-02 09:25:03.654321 | region=NODE3 | true | true | arbiter
node_mode 字段取值说明:
| 取值 | 说明 |
|---|---|
normal | 标准数据节点,存储时序业务数据。 |
arbiter | 仲裁节点,参与 Raft 投票,不存储时序业务数据。 |
查看监控指标
在KaiwuDB内置数据监控平台的 debug/chart 页面可查看以下双副本相关监控指标:
| 指标 | 说明 | 单位 |
|---|---|---|
raft.arbiter.leader.duration | 仲裁副本作为 Leader 的时长。记录每个仲裁副本从当选 Leader 开始到变回 Follower 的时间。正常情况下该值应接近 0,数值持续偏高说明数据节点可能存在故障。 | DURATION |
arbiter.rpo.sync.paused | 开启 RPO 控制后,每次执行数据同步的副本数量。 | COUNT |
错误码
| 错误码 | SQL语句 | 错误信息 |
|---|---|---|
| 0A000 | ALTER RANGE ts CONFIGURE ZONE USING arbiter_with_log = false; | unsupported FALSE value for arbiter_with_log. |
| 23514 | 前提:未设置 arbiter = true ALTER RANGE ts CONFIGURE ZONE USING arbiter_with_log = true; | could not validate zone config: must set arbiter to true before set arbiter_with_log. |