Documentation
Pglogical
pglogical 双向逻辑复制部署指南
pglogical 双向逻辑复制部署指南
本文档介绍如何在两台 PostgreSQL 16 数据库之间部署 pglogical 扩展,实现支持 TLS 的异步双向逻辑复制,并包含冲突处理、监控及与 Bucardo 的对比。
目录
- pglogical 简介
- 环境准备
- 安装 pglogical 扩展
- 配置 PostgreSQL 参数
- 创建复制用户
- 创建节点与复制集
- 建立双向订阅
- 验证复制状态
- 冲突解决策略
- TLS 配置示例
- 常用维护命令
- 监控指标
- 性能与延迟优化建议
- 优缺点总结
- 推荐部署参数模板
- 与 Bucardo 的对比
- 附录:SQL 脚本模板
pglogical 简介
| 特性 | 说明 |
|---|---|
| 类型 | 基于 WAL 的逻辑复制扩展(由 2ndQuadrant 开发,后并入 EDB/PGDG) |
| 复制粒度 | 表级 / 库级,支持选择性复制 |
| 拓扑 | 单向、一主多从、多主(双向)均可 |
| 延迟 | 秒级(异步逻辑流式复制) |
| 冲突 | 可配置(默认“先到先得”,支持自定义冲突解决) |
| DDL 支持 | 不自动复制 DDL(需两端结构一致) |
| 安全 | 继承 PostgreSQL 的 TLS / SCRAM / 证书机制 |
| 推荐版本 | PostgreSQL 13~17(pglogical 2.x/3.x) |
与 Bucardo 相比,pglogical 更现代、稳定、性能更高,且原生支持异步双向复制(multi-master)和 TLS 加密。
环境准备
假设部署架构如下:
| 节点 | 主机名 | 数据库 | 角色 |
|---|---|---|---|
| A | cn-homepage.svc.plus |
account |
node_cn |
| B | global-homepage.svc.plus |
account |
node_global |
两台节点均运行 PostgreSQL 16,并且网络互通。
安装 pglogical 扩展
在两台节点上安装 pglogical 软件包:
Ubuntu / Debian
sudo apt install postgresql-16-pglogicalRed Hat / CentOS
sudo yum install pglogical_16
安装完成后,请使用具有 SUPERUSER 权限的账号(例如 postgres)在 account 数据库中创建并验证扩展:
sudo -u postgres psql -d account -c "CREATE EXTENSION IF NOT EXISTS pglogical;"
sudo -u postgres psql -d account -c "\dx pglogical"
若缺少超级用户权限,可请数据库管理员预先创建扩展,再继续后续的 pglogical 配置。
创建 repl_user(基础复制用户)
在主库(Publisher)执行以下操作: sudo -u postgres psql
执行 SQL:
-- 创建用于逻辑/物理复制的底层用户
CREATE ROLE repl_user WITH LOGIN REPLICATION PASSWORD 'StrongPassword123!';
-- 确认创建成功
\du repl_user
输出应包含:
Role name | Attributes
-----------+-------------------------------
repl_user | Replication, Login
创建 pglogical(逻辑复制应用用户)
仍在 PostgreSQL 中执行:
-- 创建逻辑复制用的应用账户
CREATE ROLE pglogical WITH LOGIN REPLICATION PASSWORD 'StrongPass';
-- 授权访问业务数据库(假设名为 account)
GRANT ALL PRIVILEGES ON DATABASE account TO pglogical;
ALTER ROLE pglogical WITH SUPERUSER;
⚠️ 注意:pglogical 账号需要复制与读写权限,目前测试需要SUPERUSER。生产环境建议使用强密码、并限制来源 IP。
配置 PostgreSQL 参数
在两台节点的 /etc/postgresql/16/main/postgresql.conf 中设置逻辑复制所需参数:
# 修改 PostgreSQL 监听地址
listen_addresses = '*'
# 逻辑复制基础
wal_level = logical
max_wal_senders = 10
max_replication_slots = 10
max_worker_processes = 10
max_logical_replication_workers = 8
# 建议优化
shared_preload_libraries = 'pglogical'
track_commit_timestamp = on
配置访问控制(pg_hba.conf)
编辑主库(Publisher)上的 /etc/postgresql/16/main/pg_hba.conf 限定允许的远程节点:
# 本地管理
local all postgres peer
host all all 127.0.0.1/32 md5
# 允许复制与逻辑复制(加密连接)
hostssl replication repl_user <peer_ip>/32 scram-sha-256
hostssl all pglogical <peer_ip>/32 scram-sha-256
其中
启用 TLS(postgresql.conf)
scripts/generate-postgres-tls.sh
#!/usr/bin/env bash
set -e
# ============================================================
# PostgreSQL 专用 TLS 证书生成脚本(含 *.svc.plus + 双 IP)
# 作者:SVC.PLUS PostgreSQL Server TLS Generator
# ============================================================
TLS_DIR="/etc/postgres-tls"
CA_DIR="$TLS_DIR/ca"
SERVER_DIR="$TLS_DIR/server"
echo ">>> [1/6] 创建目录结构 ..."
sudo mkdir -p "$CA_DIR" "$SERVER_DIR"
cd "$TLS_DIR"
# ============================================================
# 1. 创建私有 CA 根证书
# ============================================================
echo ">>> [2/6] 生成 PostgreSQL 专用私有 CA ..."
sudo openssl genrsa -aes256 -out "$CA_DIR/ca.key.pem" 4096
sudo chmod 600 "$CA_DIR/ca.key.pem"
sudo openssl req -x509 -new -nodes -key "$CA_DIR/ca.key.pem" -sha256 -days 3650 \
-subj "/C=CN/O=SVC.PLUS PostgreSQL Authority/OU=DB Security/CN=SVC.PLUS PostgreSQL Root CA" \
-out "$CA_DIR/ca.cert.pem"
# ============================================================
# 2. 生成服务器证书
# ============================================================
echo ">>> [3/6] 生成服务器私钥与 CSR ..."
sudo openssl genrsa -out "$SERVER_DIR/server.key.pem" 2048
sudo chmod 600 "$SERVER_DIR/server.key.pem"
sudo openssl req -new -key "$SERVER_DIR/server.key.pem" \
-subj "/C=CN/O=SVC.PLUS PostgreSQL Server/OU=DB/CN=global-homepage.svc.plus" \
-out "$SERVER_DIR/server.csr.pem"
# SAN 扩展配置
cat <<EOF | sudo tee "$SERVER_DIR/server.ext" >/dev/null
basicConstraints=CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.svc.plus
DNS.2 = svc.plus
DNS.3 = global-homepage.svc.plus
DNS.4 = cn-homepage.svc.plus
IP.1 = 167.179.72.223
IP.2 = 47.120.61.35
EOF
# 签发服务器证书(2年有效)
echo ">>> [4/6] 使用 SVC.PLUS PostgreSQL Root CA 签发服务器证书 ..."
sudo openssl x509 -req -in "$SERVER_DIR/server.csr.pem" \
-CA "$CA_DIR/ca.cert.pem" -CAkey "$CA_DIR/ca.key.pem" \
-CAcreateserial -out "$SERVER_DIR/server.cert.pem" \
-days 730 -sha256 -extfile "$SERVER_DIR/server.ext"
# fullchain
sudo cat "$SERVER_DIR/server.cert.pem" "$CA_DIR/ca.cert.pem" | sudo tee "$SERVER_DIR/server.fullchain.pem" >/dev/null
# ============================================================
# 3. 安装到 PostgreSQL 标准路径
# ============================================================
echo ">>> [5/6] 安装证书到 PostgreSQL SSL 目录 ..."
sudo install -o postgres -g postgres -m 600 "$SERVER_DIR/server.key.pem" /etc/ssl/private/svc.plus-postgres.key
sudo install -o postgres -g postgres -m 644 "$SERVER_DIR/server.fullchain.pem" /etc/ssl/certs/svc.plus-postgres.crt
sudo install -o postgres -g postgres -m 644 "$CA_DIR/ca.cert.pem" /etc/ssl/certs/svc.plus-postgres-ca.crt
# ============================================================
# 4. 输出后续操作提示
# ============================================================
echo "==============================================================="
echo "✅ [SVC.PLUS PostgreSQL TLS] 已生成并安装完成"
echo ""
echo "请在 /etc/postgresql/16/main/postgresql.conf 中添加或确认以下配置:"
echo ""
echo " ssl = on"
echo " ssl_cert_file = '/etc/ssl/certs/svc.plus-postgres.crt'"
echo " ssl_key_file = '/etc/ssl/private/svc.plus-postgres.key'"
echo " ssl_ca_file = '/etc/ssl/certs/svc.plus-postgres-ca.crt'"
echo ""
echo "⚙️ 然后执行: sudo systemctl restart postgresql"
echo ""
echo "📦 客户端(订阅端)请复制 CA 根证书:"
echo " /etc/postgres-tls/ca/ca.cert.pem"
echo "至客户端路径:"
echo " /var/lib/postgresql/.postgresql/root.crt"
echo "(权限:600,属主 postgres)"
echo ""
echo "🔍 验证命令示例:"
echo " openssl s_client -connect 167.179.72.223:5432 -starttls postgres -servername global-homepage.svc.plus"
echo ""
echo "👑 证书主题:SVC.PLUS PostgreSQL Server"
echo "包含 SAN: *.svc.plus, global-homepage, cn-homepage, IP(167.179.72.223, 47.120.61.35)"
echo "==============================================================="
创建好的证书分发到在两台节点上
两台节点上执行:
PostgreSQL 客户端必须存在一份 受信任 CA 根证书文件, 路径固定为 /var/lib/postgresql/.postgresql/root.crt (属于 postgres 用户,权限必须是 600)。
# 1️⃣ 创建目录
sudo -u postgres mkdir -p /var/lib/postgresql/.postgresql
# 2️⃣ 从 global-homepage 拉取服务器的 CA 根证书
cp /etc/ssl/certs/svc.plus-postgres-ca.crt /var/lib/postgresql/.postgresql/root.crt
# 3️⃣ 设置权限
sudo chown postgres:postgres /var/lib/postgresql/.postgresql/root.crt
sudo chmod 600 /var/lib/postgresql/.postgresql/root.crt
在两台节点上执行:编辑 /etc/postgresql/16/main/postgresql.conf 检查下面配置是否存在
ssl = on
ssl_cert_file = '/etc/ssl/certs/svc.plus.crt'
ssl_key_file = '/etc/ssl/private/svc.plus.key'
重启 PostgreSQL 生效
sudo systemctl restart postgresql@16-main 或(简写方式): sudo systemctl restart postgresql
验证角色与访问
1️⃣ 查看角色列表 sudo -u postgres psql -c "\du" 应看到:
repl_user | Replication, Login pglogical | Replication, Login
2️⃣ 订阅端测试连接
在另一台节点测试 TLS 登录: psql "host=
成功进入 account=> 提示符表示逻辑复制用户配置完毕 ✅。
创建节点与复制集
双向架构概览
┌───────────────────────────┐
│ 🌍 global-homepage │
│ node_name = node_global │
│ publishes → node_cn │
│ subscribes ← node_cn │
└───────────────────────────┘
▲ │
│ ▼
┌───────────────────────────┐
│ 🇨🇳 cn-homepage │
│ node_name = node_cn │
│ publishes → node_global │
│ subscribes ← node_global│
└───────────────────────────┘
两个节点都:
- 拥有 pglogical 扩展;
- 注册自己的 node;
- 定义相同的 replication_set;
- 创建互为订阅(create_subscription)。
步骤 1:CN 节点初始化
登录 CN 主机(cn-homepage.svc.plus):
执行: sudo -u postgres psql -d account 执行:
-- 启用扩展
CREATE EXTENSION IF NOT EXISTS pglogical;
-- 注册本地节点
SELECT pglogical.create_node(
node_name := 'node_cn',
dsn := 'host=47.120.61.35 port=5432 dbname=account user=pglogical password=StrongPass sslmode=prefer'
);
-- 创建复制集
SELECT pglogical.create_replication_set('rep_all');
SELECT pglogical.replication_set_add_all_tables('rep_all', ARRAY['public']);
步骤 2:Global 节点初始化
登录 Global 主机(global-homepage.svc.plus):
执行:sudo -u postgres psql -d account 执行:
-- 启用扩展
CREATE EXTENSION IF NOT EXISTS pglogical;
-- 注册本地节点
SELECT pglogical.create_node(
node_name := 'node_global',
dsn := 'host=167.179.72.223 port=5432 dbname=account user=pglogical password=StrongPass sslmode=prefer'
);
-- 创建复制集
SELECT pglogical.create_replication_set('rep_all');
SELECT pglogical.replication_set_add_all_tables('rep_all', ARRAY['public']);
步骤 3:建立双向订阅
- 在 CN 节点 上创建订阅(订阅 Global)
SELECT pglogical.create_subscription(
subscription_name := 'sub_from_global',
provider_dsn := 'host=167.179.72.223 port=5432 dbname=account user=pglogical password=StrongPass sslmode=prefer',
replication_sets := ARRAY['rep_all'],
synchronize_structure := false,
synchronize_data := true,
forward_origins := '{}'
);
- 在 Global 节点 上创建订阅(订阅 CN)
SELECT pglogical.create_subscription(
subscription_name := 'sub_from_cn',
provider_dsn := 'host=47.120.61.35 port=5432 dbname=account user=pglogical password=StrongPass sslmode=prefer',
replication_sets := ARRAY['rep_all'],
synchronize_structure := false,
synchronize_data := true,
forward_origins := '{}'
);
参数解释
参数 含义
- synchronize_structure=false 表示两端表结构已经一致,不再自动创建表。
- synchronize_data=true 首次订阅时自动同步现有数据。
- forward_origins='{}' 防止环形复制(即从对方同步的数据再传回去)。
- sslmode=verify-full 使用 TLS 校验证书和域名。
检查状态
两端都执行:
SELECT * FROM pglogical.node;
SELECT * FROM pglogical.subscription;
SELECT * FROM pglogical.show_subscription_status();
正常情况下你会看到:
各自注册的 node(node_cn / node_global) 一条订阅(sub_from_cn / sub_from_global)
状态为 “replicating”
🚦 常见问题排查
错误 原因 解决
current database is not configured as pglogical node 没有先执行 create_node() 先执行 pglogical.create_node()
could not connect to server 对方 pg_hba.conf 未放行 检查 hostssl all pglogical
验证复制状态
常用验证命令:
SELECT * FROM pglogical.show_subscription_status();
SELECT * FROM pglogical.show_node_info();
查看流复制进度:
SELECT application_name, state, sent_lsn, write_lsn, flush_lsn, replay_lsn
FROM pg_stat_replication;
日志中出现如下信息表明表同步完成:
pglogical: initial copy of table "public.users" finished
冲突解决策略
pglogical 默认策略为 “first commit wins”。在开启 track_commit_timestamp = on 后,可以使用以下策略:
| 策略 | 含义 |
|---|---|
error |
发生冲突时报错并终止复制 |
apply_remote |
使用远端数据覆盖本地 |
keep_local |
保留本地数据,忽略远端变更 |
latest_commit |
保留提交时间更晚的行 |
custom |
调用自定义函数处理冲突 |
示例:
SELECT pglogical.alter_subscription_options(
subscription_name := 'sub_from_b',
options := '{conflict_resolution=latest_commit}'
);
TLS 配置示例
使用 libpq 连接参数即可启用 TLS:
SELECT pglogical.create_subscription(
subscription_name := 'sub_from_b',
provider_dsn := 'host=pgB.svc.plus port=5432 dbname=account user=pglogical password=StrongPass sslmode=verify-full sslrootcert=/etc/ssl/rootCA.crt sslcert=/etc/ssl/client.crt sslkey=/etc/ssl/client.key',
replication_sets := ARRAY['rep_all']
);
sslmode 支持 require、verify-ca、verify-full,推荐使用 verify-full 并确保证书 CN/SAN 与主机名匹配。
常用维护命令
| 操作 | SQL 命令 |
|---|---|
| 暂停订阅 | SELECT pglogical.alter_subscription_disable('sub_from_global'); |
| 恢复订阅 | SELECT pglogical.alter_subscription_enable('sub_from_global', true); |
| 删除订阅 | SELECT pglogical.drop_subscription('sub_from_global'); |
| 删除节点 | SELECT pglogical.drop_node('node_cn'); |
监控指标
| 表 / 视图 | 说明 |
|---|---|
pglogical.show_subscription_status() |
订阅状态(延迟、复制槽、错误) |
pg_stat_replication |
WAL 流复制进度 |
pglogical.replication_set |
当前同步的表集合 |
pglogical.local_sync_status |
同步阶段(initial / catching-up / ready) |
性能与延迟优化建议
| 参数 | 推荐值 | 说明 |
|---|---|---|
max_replication_slots |
≥ 10 | 允许更多并发订阅 |
max_wal_senders |
≥ 10 | 支持更多并发流复制连接 |
maintenance_work_mem |
≥ 128MB | 提高初始数据复制效率 |
synchronous_commit |
off |
降低写入延迟(异步复制场景) |
wal_compression |
on |
降低网络传输量 |
subscription_apply_delay |
0–60 秒 | 可配置延迟重放,满足业务需求 |
优缺点总结
| 优点 | 缺点 |
|---|---|
| 原生逻辑复制,性能远优于 Bucardo | 不复制 DDL,需保证结构一致 |
| 支持 TLS / SCRAM / 双向复制 | 需要安装扩展(非纯 SQL) |
冲突处理策略灵活(latest_commit / custom) |
不适合同一行的高并发双写场景 |
| 延迟低(秒级) | 不支持系统表复制 |
推荐部署参数模板
| 项 | 配置 |
|---|---|
| 节点 A/B | PostgreSQL 16 + pglogical 3.6 |
| 通道 | TLS (sslmode=verify-full) |
| 复制方向 | 双向 |
| 延迟 | 2–10 秒 |
| 冲突策略 | latest_commit |
| 初始同步 | synchronize_data = true |
| 同步集 | 业务表(users、identities、sessions) |
| DDL 管理 | GitOps + 同步迁移脚本 |
| 监控 | Grafana + pg_stat_replication + pglogical 状态视图 |
与 Bucardo 的对比
| 维度 | pglogical | Bucardo |
|---|---|---|
| 复制机制 | WAL 逻辑流 | 触发器 + 队列 |
| 延迟 | 秒级 | 秒级至分钟级 |
| 性能 | 高 | 中 |
| 冲突控制 | 内置多策略 | Perl 自定义 |
| 安全 | 原生支持 TLS | 依赖 libpq TLS |
| 部署复杂度 | 中(需扩展) | 低(Perl 脚本) |
| 推荐场景 | 跨 Region 双向 / 实时异步复制 | 异地多活、低写负载场景 |
附录:SQL 脚本模板
可将上述配置整理为以下 SQL 脚本:
setup-node-a.sql
-- 节点 A 初始化
CREATE EXTENSION IF NOT EXISTS pglogical;
SELECT pglogical.create_node(
node_name := 'node_a',
dsn := 'host=pgA.svc.plus port=5432 dbname=account user=pglogical password=StrongPass sslmode=verify-full'
);
SELECT pglogical.create_replication_set('rep_all');
SELECT pglogical.replication_set_add_all_tables('rep_all', ARRAY['public']);
SELECT pglogical.create_subscription(
subscription_name := 'sub_from_b',
provider_dsn := 'host=pgB.svc.plus port=5432 dbname=account user=pglogical password=StrongPass sslmode=verify-full',
replication_sets := ARRAY['rep_all'],
synchronize_structure := false,
synchronize_data := true,
forward_origins := '{}'
);
setup-node-b.sql
-- 节点 B 初始化
CREATE EXTENSION IF NOT EXISTS pglogical;
SELECT pglogical.create_node(
node_name := 'node_b',
dsn := 'host=pgB.svc.plus port=5432 dbname=account user=pglogical password=StrongPass sslmode=verify-full'
);
SELECT pglogical.create_replication_set('rep_all');
SELECT pglogical.replication_set_add_all_tables('rep_all', ARRAY['public']);
SELECT pglogical.create_subscription(
subscription_name := 'sub_from_a',
provider_dsn := 'host=pgA.svc.plus port=5432 dbname=account user=pglogical password=StrongPass sslmode=verify-full',
replication_sets := ARRAY['rep_all'],
synchronize_structure := false,
synchronize_data := true,
forward_origins := '{}'
);
verify-replication.sql
-- 验证订阅状态
SELECT * FROM pglogical.show_subscription_status();
SELECT * FROM pglogical.show_node_info();
-- 检查复制进度
SELECT application_name, state, sent_lsn, write_lsn, flush_lsn, replay_lsn
FROM pg_stat_replication;
以上脚本可根据实际业务需要调整数据库名称、节点信息及复制集内容。
Feedback