pgsql-34 主从复制与高可用
1. 概述
复制(Replication)是高可用和读扩展的基础。PostgreSQL 的复制建立在 WAL与检查点机制 之上:主库把 WAL 传给从库重放,从而保持数据一致。
复制解决两类问题:
- 高可用(HA):主库宕机时从库顶上,减少停机。
- 读扩展:读请求分流到从库,减轻主库压力。
对比 MySQL:MySQL 主从基于 binlog(逻辑日志)传输并在从库重放;PostgreSQL 流复制基于 WAL(物理日志)。MySQL 复制默认异步、也支持半同步;PG 支持异步和同步复制。两者概念相通,但 PG 的物理流复制是"字节级完全一致的副本",MySQL binlog 复制是"逻辑重放"。
2. 复制的两大维度
2.1 物理复制 vs 逻辑复制
| 维度 | 物理复制(流复制) | 逻辑复制 |
|---|---|---|
| 传输内容 | WAL 字节流(整实例) | 解码后的行变更(按表/发布) |
| 粒度 | 整个实例,全量一致 | 可只复制部分表 |
| 跨版本 | 需同大版本 | 可跨版本(升级利器) |
| 从库可写 | 只读 | 可写(双向/聚合场景) |
| 典型用途 | HA、读副本 | 部分同步、跨版本升级、数据分发 |
2.2 同步 vs 异步
| 模式 | 提交何时返回 | 数据安全 | 性能 |
|---|---|---|---|
| 异步(默认) | 主库写完 WAL 即返回 | 主库宕机可能丢最后几笔 | 快 |
| 同步 | 等至少一个从库确认收到 WAL | 不丢已提交数据 | 慢(受网络影响) |
# 同步复制配置(主库)
synchronous_standby_names = 'FIRST 1 (standby1, standby2)' # 至少1个从库确认
synchronous_commit = on
3. 搭建流复制(物理)
3.1 主库配置
# postgresql.conf
wal_level = replica
max_wal_senders = 10 # 允许的复制连接数
wal_keep_size = 1GB # 保留WAL供从库追赶(或用复制槽更可靠)
# pg_hba.conf:允许从库以复制身份连接
host replication replicator 10.0.0.0/24 md5
-- 创建复制专用角色
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'xxx';
3.2 复制槽(推荐)
复制槽(replication slot)确保主库在从库确认前不删除对应 WAL,避免从库落后太多后追不上。
SELECT pg_create_physical_replication_slot('standby1_slot');
3.3 从库搭建
# 用基础备份初始化从库,-R 自动生成复制连接配置
pg_basebackup -h primary_host -D $PGDATA -U replicator -P -R \
-X stream -S standby1_slot
# -R 会自动写入 primary_conninfo 并创建 standby.signal
pg_ctl start # 启动即进入流复制
4. 监控复制状态
-- 主库上看各从库同步情况和延迟
SELECT client_addr, state, sync_state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS 延迟字节
FROM pg_stat_replication;
-- 从库上看复制延迟(时间)
SELECT now() - pg_last_xact_replay_timestamp() AS 复制延迟;
5. 读写分离
从库默认只读,可承接读请求:
-- 从库上查询会报错的写操作
INSERT INTO t VALUES (1); -- ERROR: cannot execute INSERT in a read-only transaction
读写分离实现方式:
- 应用层:写连主库、读连从库(代码或 ORM 路由)。
- 中间件:Pgpool-II、或在 PgBouncer 前加路由层。
- ⚠️ 复制延迟坑:异步复制下从库可能读到旧数据(写完立刻读从库读不到)。对一致性敏感的读要走主库,或用同步复制。
6. 自动故障转移与高可用
流复制本身不会自动切换主库——主库挂了需要有机制把某个从库提升为新主库并让应用重连。生产用专门的 HA 方案:
| 方案 | 说明 |
|---|---|
| Patroni | 最流行。用 etcd/Consul/ZooKeeper 做分布式选主,自动故障转移 + 配置管理 |
| repmgr | 轻量,复制管理和手动/自动故障转移 |
| Pgpool-II | 连接池 + 负载均衡 + 故障转移一体 |
| 云托管 | RDS/云数据库自带高可用,最省心 |
Patroni 架构:每个 PG 节点跑一个 Patroni 进程,通过 etcd 竞争"leader key",持有者为主库;主库失联后其他节点重新选主并提升,配合 HAProxy/VIP 让应用透明重连。
# patroni.yml 关键片段
scope: pg-cluster
etcd:
hosts: 10.0.0.10:2379
postgresql:
data_dir: /var/lib/postgresql/data
parameters:
max_wal_senders: 10
7. 面试高频问答
Q1:PostgreSQL 流复制基于什么?和 MySQL 有何不同? 基于 WAL 物理日志传输并在从库重放,是字节级完全一致的副本;MySQL 基于 binlog 逻辑日志重放。PG 也有逻辑复制(解码 WAL 成行变更),可按表复制、跨版本。
Q2:同步复制和异步复制怎么选? 异步性能好但主库宕机可能丢最后几笔已提交数据;同步不丢数据但每次提交要等从库确认、受网络影响慢。金融等强一致场景用同步,一般场景用异步。
Q3:物理复制和逻辑复制的区别? 物理复制传 WAL 字节流、整实例一致、从库只读、需同版本;逻辑复制传解码后的行变更、可只复制部分表、可跨版本、从库可写。跨大版本升级常用逻辑复制。
Q4:复制槽有什么用? 保证主库在从库确认消费前不回收对应 WAL,防止从库落后过多导致所需 WAL 被删而无法追赶。
Q5:流复制能自动故障转移吗?怎么实现 HA? 不能,流复制只负责数据同步。自动故障转移需 Patroni(+etcd)、repmgr 或 Pgpool-II 等方案选主并提升从库,配合 VIP/HAProxy 让应用重连。
Q6:读写分离要注意什么? 异步复制存在延迟,从库可能读到旧数据。对一致性敏感的"写后立即读"应走主库,或采用同步复制/延迟检测。
8. 下一步
学习 监控与日志管理,保障数据库可观测性。
xingliuhua