分组复制
分组复制(Multi-group Replication)在传统流复制的基础上,将多个备库划分到不同分组,为每组配置独立的同步级别(synchronous_commit),实现差异化的复制可靠性策略。
传统流复制只能为整个集群配置单一全局同步级别,难以适配"同机房严格同步 + 跨机房宽松同步"的混合部署场景。分组复制通过引入 synchronous_standby_namesX 与 synchronous_commitX(X 为 1~4)参数,将备库划分为不同分组:主分组承担核心同步任务,额外分组适配异地容灾等差异化场景。
核心规则:所有额外分组 synchronous_commitX 的同步级别,必须低于或等于主分组 synchronous_commit 的同步级别。
典型应用是同城灾备:将同中心备节点的同步级别设为 remote_apply(最强,主库提交即备库可见,故障切换 RPO=0),同城灾备中心备节点的同步级别设为 on(次强,跳过 replay 等待,释放主库性能),从而既保证同中心数据零丢失,又不让跨机房 replay 拖累主库提交延迟。
主要参数
分组复制除主分组外,最多可设置四个额外分组。涉及参数分为两类:
定义同步备库分组的参数(哪些备库属于哪个分组、需要多少台确认):
synchronous_standby_names 主分组
synchronous_standby_names1 额外分组 1
synchronous_standby_names2 额外分组 2
synchronous_standby_names3 额外分组 3
synchronous_standby_names4 额外分组 4
定义事务提交同步级别的参数(事务提交需等待备库完成到哪个阶段):
synchronous_commit 对应主分组
synchronous_commit1 对应额外分组 1
synchronous_commit2 对应额外分组 2
synchronous_commit3 对应额外分组 3
synchronous_commit4 对应额外分组 4
同步级别由强到弱为:remote_apply > on > remote_write > local / off。
说明:编号一一对应,synchronous_standby_names1 对应 synchronous_commit1。分组格式 '数量(备库名1, 备库名2, ...)' 表示等待括号内任意"数量"台备库确认即可,例如 '1(node2,node4)' 表示等待 node2、node4 中任意一台满足同步条件。
服务器规划
以一台主库、三台备库为例,演示主分组 + 一个额外分组的配置:
| 节点 | 角色 | 所属分组 | 同步级别 |
|---|---|---|---|
node1(${主库IP}) | 主库(Primary) | — | — |
node2(${备库2IP}) | 备库 | 主分组 | remote_apply |
node3(${备库3IP}) | 备库 | 额外分组 1 | on |
node4(${备库4IP}) | 备库 | 主分组 | remote_apply |
数据流向为单向:主库向所有备库同步,确保数据一致性。
为便于节点间通过主机名互访,需在所有节点的 /etc/hosts 中添加各节点的主机名与 IP 映射;这是复制槽(Replication Slot)与 application_name 正常工作的基础前提。
配置步骤
执行环境说明:以下命令与输出基于 Halo 1.0.16 实测。示例中 ${主库IP} 等为占位符,部署时替换为实际 IP;密码请使用实际强密码。
主库准备
1)创建流复制用户
在主库创建具备 REPLICATION 属性的专用用户:
halo0root=# CREATE USER replica WITH PASSWORD '${实际密码}' REPLICATION;
CREATE ROLE
2)配置分组同步参数
修改主库 postgresql.conf,定义主分组与额外分组 1 的规则和同步级别:
synchronous_standby_names = '1(node2,node4)'
synchronous_standby_names1 = '1(node3)'
synchronous_commit = remote_apply
synchronous_commit1 = on
主分组 '1(node2,node4)' 采用 remote_apply,表示 node2、node4 中任一台完成日志应用即视为同步成功;额外分组 1 '1(node3)' 采用 on,表示 node3 完成 WAL 落盘即视为成功。
3)配置访问授权
修改主库 pg_hba.conf,允许备库网段发起复制连接:
host replication replica ${备库网段} md5
4)重载生效
pg_ctl reload
server signaled
说明:修改 pg_hba.conf 与 synchronous_* 系列参数,数据库只需 reload 即可生效,无需重启。
搭建备库
在三台备库上分别使用 pg_basebackup 从主库克隆数据,并为每台备库创建唯一的物理复制槽:
# node2 上执行
pg_basebackup -F p -X stream -v -P -h ${主库IP} -U replica \
-D $PGDATA -R -C --slot node2_slot
# node3 上执行
pg_basebackup -F p -X stream -v -P -h ${主库IP} -U replica \
-D $PGDATA -R -C --slot node3_slot
# node4 上执行
pg_basebackup -F p -X stream -v -P -h ${主库IP} -U replica \
-D $PGDATA -R -C --slot node4_slot
说明:--slot=nodeX_slot 为每台备库创建唯一复制槽,防止主库过早删除备库尚未拉取的 WAL 日志,保障复制连续性。-R 会自动生成 postgresql.auto.conf 并写入主库连接信息。
备库参数调整
执行 pg_basebackup -R 时系统自动生成 postgresql.auto.conf,其中包含 primary_conninfo(主库连接信息)。为实现对备库分组的精确控制,需在每个备库的 primary_conninfo 中追加 application_name 参数,其值必须与主库 synchronous_standby_names 中定义的名称完全一致——这是备库被正确分组的唯一依据。
各备库 postgresql.auto.conf 中的 primary_conninfo 配置如下:
# node2
primary_conninfo = 'user=replica password=${实际密码} host=${主库IP} port=1921 application_name=node2'
primary_slot_name = 'node2_slot'
# node3
primary_conninfo = 'user=replica password=${实际密码} host=${主库IP} port=1921 application_name=node3'
primary_slot_name = 'node3_slot'
# node4
primary_conninfo = 'user=replica password=${实际密码} host=${主库IP} port=1921 application_name=node4'
primary_slot_name = 'node4_slot'
启动备库并重载
依次启动三台备库:
pg_ctl start
若备库已在运行(例如修改 postgresql.auto.conf 后),执行重载使配置立即生效,无需重启:
pg_ctl reload
至此,分组复制配置完成。
验证
查看分组复制状态
在主库查询 pg_stat_replication,查看各备库的分组归属与同步状态:
halo0root=# SELECT application_name, client_addr, state, sync_state, sync_priority
FROM pg_stat_replication;
预期输出(与实际部署一致):
application_name | client_addr | state | sync_state | sync_priority
------------------+---------------+-----------+--------------+---------------
node2 | ${备库2IP} | streaming | sync[0] | 1
node3 | ${备库3IP} | streaming | sync[1] | 1
node4 | ${备库4IP} | streaming | potential[0] | 2
(3 rows)
各状态含义:
- node2 为
sync[0]:主分组的首个同步备库,当前承担主分组的同步确认; - node3 为
sync[1]:额外分组 1 的同步备库; - node4 为
potential[0]:主分组的潜在同步备库,当 node2 不可用时接替其同步角色(主分组'1(node2,node4)'只需 1 台确认,故未承担确认的 node4 显示为 potential)。
确认分组参数配置:
halo0root=# SHOW synchronous_standby_names;
halo0root=# SHOW synchronous_standby_names1;
halo0root=# SHOW synchronous_commit;
halo0root=# SHOW synchronous_commit1;
synchronous_standby_names
---------------------------
1(node2,node4)
(1 row)
synchronous_standby_names1
----------------------------
1(node3)
(1 row)
synchronous_commit
--------------------
remote_apply
(1 row)
synchronous_commit1
---------------------
on
(1 row)
验证数据同步
在主库写入测试数据:
halo0root=# CREATE TABLE mgr_test(id int, node text);
CREATE TABLE
halo0root=# INSERT INTO mgr_test VALUES (1, 'written_on_node1');
INSERT 0 1
在三台备库上查询,确认数据均已同步:
-- 在备库执行
SELECT * FROM mgr_test;
id | node
----+------------------
1 | written_on_node1
(1 row)
说明:上述验证表明,主库的写操作经分组复制同步到了所有备库,且数据一致。其中主分组(node2/node4)采用 remote_apply 保障同中心 RPO=0,额外分组(node3)采用 on 在保证数据不丢失的同时降低跨节点同步对主库性能的影响。