基于双活互备的 DR 解决方案
GreptimeDB 企业版可以将两个 Standalone 节点部署为对等节点。两个节点都能接受读写、保存完整的数据副本,并将数据变更异步复制到对端,不存在长期固定的主节点。
该拓扑适用于边缘和中小型部署:无需运维 GreptimeDB 分布式集群,也能获得节点级或站点级容灾能力。负载均衡器、客户端驱动或服务发现系统负责将流量发送到可用节点。
架构
两个节点相互独立地提供服务:
- 写入在接收请求的节点提交,随后复制到对端;客户端无需等待对端确认。
- 查询只在接收请求的节点上执行,GreptimeDB 不会合并两个节点的查询结果。
- 两个节点之间不要求网络持续连通。对端或网络不可用时,待同步的数据变更会保留在本地,并在连接恢复后继续发送;前提是源节点的存储仍然可用且容量充足。
- 从对端复制而来的数据变更不会再次发回源节点,从而避免循环复制。
由于复制是异步的,对端可能暂时读到旧数据。需要 read-after-write 一致性的应用,应将相关读取保持在接受写入的节点,或等待复制追平后再切换节点。
能力与边界
| 能力 | 行为 |
|---|---|
| 节点与站点冗余 | 每个节点保存完整的数据副本,并能对外提供服务。 |
| 写入复制 | 数据变更在两个节点之间双向异步复制。 |
| 查询执行 | 查询在接收请求的节点上本地执行。 |
| 网络中断 | 健康节点继续服务;对端恢复后继续同步待处理的数据变更。 |
| 流量切换 | 由外部负载均衡器、客户端驱动或服务发现系统切换端点。 |
| 一致性 | 两个节点的数据最终一致。 |
| 横向扩展 | 该拓扑提供双节点冗余,不能替代分布式集群。 |
规划 DR 时需要考虑以下限制:
- 客户端写入成功不代表对端已经收到该写入。
- 如果待同步的数据变更到达对端前,源节点及其本地存储永久丢失,这部分数据可能无法从对端恢复。
- 对端不可用期间执行的 Schema 变更,可能需要在恢复后核验或手动补偿。
- 该拓扑不会选举流量主节点,也不会自动切换客户端连接。
如果业务需要同步复制、单站点本地存储完全丢失后仍保持严格的零 RPO,或者需要横向扩展,请改用基于 Remote WAL 和跨区域对象存储的分布式 DR 架构。
规划部署
启用双活互备前:
- 将两个节点部署到不同的故障域;对于站点级容灾,应使用不同的可用区或区域。
- 为每个节点准备足以保存完整数据副本的存储,并预留能够覆盖计划中最长对端故障时间的空间。
- 选择外部流量切换机制,并明确健康检查、重试策略和连接排空行为。
- 为两个节点分配互不重叠的 table ID 范围,避免节点独立建表时发生 ID 冲突。
- 明确应用如何处理结果不确定的写入和重试;应尽可能使用幂等写入。
配置
在每个节点上将另一个节点配置为同步目标。以下示例展示节点 A 上的相关配置:
[[sync_nodes]]
name = "node-b"
enabled = true
[sync_nodes.metasrv]
metasrv_addrs = ["10.0.2.10:3002"]
[sync_nodes.region_server]
id = 2
addr = "10.0.2.10:4001"
[table_id_range]
start = 1024
end = 1000000
在节点 B 上配置节点 A 的端点,并使用不同的 table_id_range。每个 sync_nodes.name 必须唯一,并且只能包含 ASCII 字母、数字、_ 或 -。
实际端点和 table ID 范围取决于部署环境。生产配置应联系 Greptime 进行审核。
故障与恢复行为
| 事件 | 预期行为 | 运维操作 |
|---|---|---|
| 对端或站点间网络不可用 | 健康节点继续服务,待处理的数据变更等待对端恢复。 | 保持流量在健康节点,并监控复制状态和本地存储容量。 |
| 一个节点不可用 | 外部切换机制将流量转走之前,发送到该节点的请求会失败。 | 确认存活节点健康,然后切换或排空流量。 |
| 不可用节点恢复 | 待处理的数据变更自动同步;追平前两个节点的数据可能不同。 | 保持流量稳定,核验数据和 Schema 后再恢复常规路由。 |
| 源节点本地存储空间耗尽或不可用 | 为保证数据可恢复性,新的写入可能被拒绝。 | 恢复本地存储容量,并根据应用语义重试失败的写入。 |
| 节点及其存储永久丢失 | 尚未复制的数据变更可能不在存活节点上。 | 如果存在其他数据副本,从中恢复,并评估实际 RPO。 |
切换流量
节点或站点故障时:
- 确认备用节点可访问,并能执行关键查询。
- 停止向故障端点发送新流量。
- 通过选定的故障切换机制将流量发送到健康节点。
- 核验应用错误率、写入成功率和关键查询结果。
- 在故障节点恢复并追平数据之前,保持路由稳定。
两个节点无法通信时,应避免反复切换写流量。虽然两个节点都能接受写入,但断连期间在两端分别写入会增加需要收敛的状态,并提高恢复核验的复杂度。
恢复节点
故障节点或网络恢复后:
- 继续将应用写入发送到健康节点。
- 恢复故障节点及节点间的同步连接。
- 等待复制状态恢复正常,并处理完积压的数据变更。
- 对比两个节点上的关键表 Schema 和近期数据。
- 对恢复节点执行应用级读写探测。
- 逐步恢复流量,并持续监控两个节点。
应单独核验故障期间发生的 Schema 变更。关键表的 Schema 一致前,不要将生产流量恢复到该节点。
RPO 与 RTO
RPO
RPO 取决于故障类型和等待复制的数据量:
- 如果只有对端或网络故障,并且源节点存储仍然完整,待处理的数据变更可以在恢复后继续发送。
- 如果复制完成前源节点及其存储丢失,存活节点可能不包含待处理的数据变更。
该拓扑不提供同步复制模式。应在具有代表性的负载下测量复制状态,并将观察到的延迟纳入 DR 计划。
RTO
RTO 包括故障检测、端点切换、连接重试和应用恢复时间。GreptimeDB 不在该双节点拓扑内执行流量切换,因此外部流量层及其配置决定了大部分 RTO。
应定期演练计划内和计划外的故障切换。演练需要覆盖存量连接、连接池、正在执行的写入,以及核验备用节点所需的时间。
选择流量切换机制
-
负载均衡器。 配置主动健康检查,并从转发列表中移除故障端点。托管负载均衡器或独立的 HAProxy 实例可以将切换策略与应用解耦。
-
客户端驱动。 一些 MySQL 和 PostgreSQL 驱动支持配置多个主机,并在连接失败时尝试其他端点。例如参见 MySQL Connector/J failover 和 PostgreSQL JDBC connection failover。连接故障切换并不保证重试非幂等写入是安全的。
-
服务发现或端点更新。 健康检查系统可以更新 DNS、服务注册表或应用的端点集合。估算 RTO 时,需要计入健康检查间隔、DNS TTL 和连接池刷新时间。
HAProxy 示例
以下最小配置通过 127.0.0.1:14000 上的本地 TCP 端点代理 GreptimeDB HTTP API。节点 A 健康时,HAProxy 将流量发送到节点 A;连续三次健康检查失败后切换到节点 B。节点 A 连续两次健康检查成功后,会重新成为可选节点。
虽然两个 GreptimeDB 节点都能接受写入,但该示例使用主备流量路由,使应用在正常情况下只向一个节点写入。请根据实际环境替换主机名、端口、监听地址和超时时间。
global
log /dev/log local0
log /dev/log local1 notice
daemon
defaults
mode tcp
log global
option tcplog
timeout connect 5s
timeout client 30s
timeout server 30s
frontend greptimedb_http
bind 127.0.0.1:14000
default_backend greptimedb_http_nodes
backend greptimedb_http_nodes
# Preferred node
server node-a node-a:4000 check inter 2s fall 3 rise 2
# Failover node
server node-b node-b:4000 check inter 2s fall 3 rise 2 backup
该配置只代理端口 4000 上的 HTTP API。如需代理 MySQL 或 PostgreSQL 协议,请分别创建独立的 frontend 和 backend,并将目标端口设置为 4002 或 4003。
启动 HAProxy 前先检查配置:
haproxy -c -f haproxy.cfg
haproxy -f haproxy.cfg
运维检查清单
应监控两个节点的可用性、复制错误、复制延迟和存储容量,并通过端到端探针向一个节点写入数据,再从对端核验结果。
投入生产前,确认:
- 两个节点都能正常读写;
- 任一方向的数据变更都能到达对端;
- 在任一节点建表都不会发生 table ID 冲突;
- 流量能在目标 RTO 内完成切换;
- 长时间网络中断后,积压的数据变更能够追平;
- 恢复节点接收流量前,关键 Schema 和近期数据已经一致;
- 应用能够安全处理连接中断和结果不确定的写入。
请参考解决方案比较,比较不同 DR 方案的 RPO、RTO 和基础设施要求。