ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 一主两从搭建及同步模式深度详解

PostgreSQL 一主两从搭建及同步模式深度详解

原创 PostgreSQL 作者:圣彼得堡的村长 时间:2026-09-10 10:16:56 0 删除 编辑

PostgreSQL 一主两从搭建及同步模式深度详解

  本打算介绍一下 GaussDB 集中式集群的同步机制,但考虑到其底层本质上仍基于 PostgreSQL 流复制,因此先“回归本源”, 介绍 PostgreSQL 一主两从集群的搭建过程,并详细解析 synchronous_standby_names 参数配置为 ANY、 FIRST 和 * 场景下复制模式的差异,为后面介绍 GaussDB 复制技术奠定基础。

1. 环境准备

1.1 节点规划

主机名 IP地址 角色作用
pgsql1 192.168.182.146 主库
pgsql2 192.168.182.147 从库1
pgsql3 192.168.182.148 从库2

1.2 设置主机名

hostnamectl set-hostname pgsql1
hostnamectl set-hostname pgsql2
hostnamectl set-hostname pgsql3

1.3 配置 hosts 文件

cat >> /etc/hosts <<EOF
192.168.182.146 pgsql1 
192.168.182.147 pgsql2
192.168.182.148 pgsql3
EOF

1.4 关闭防火墙和 SELINUX

systemctl status firewalld
systemctl stop firewalld
systemctl disable firewalld
systemctl status firewalld
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config

1.5 创建 postgres 用户

useradd postgres
echo 'postgres' | passwd --stdin postgres

1.6 配置资源限制

cat >> /etc/security/limits.conf <<EOF
postgres soft nproc unlimited
postgres hard nproc unlimited
postgres soft nofile 100000
postgres hard nofile 100000
postgres soft stack unlimited
postgres hard stack unlimited
postgres soft core unlimited
postgres hard core unlimited
postgres soft memlock unlimited
postgres hard memlock unlimited
EOF

1.7 创建数据目录

rm -rf /data/*
mkdir -p /pgsql/{pghome}
mkdir -p /data/{pgwal,pgdata,pgbak}
mkdir -p /data/pgwal/archive_wals
mkdir -p /data/pgdata/pg13
chown -R postgres:postgres /pgsql
chown -R postgres:postgres /data/
chmod -R 700 /pgsql
chmod -R 700 /data/{pgwal,pgdata,pgbak}

1.8 配置环境变量

su - postgres
cat >> /home/postgres/.bash_profile <<EOF
export PGHOME=/pg/pghome
export PGDATA=/data/pgdata/pg13
export PGPORT=5432
export PGDATABASE=postgres
export LD_LIBRARY_PATH=\$PGHOME/lib:\$LD_LIBRARY_PATH
export PATH=\$PGHOME/bin:\$PATH
EOF

1.9 配置 yum 源 && 安装依赖包

sudo wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
yum -y install wget gcc gcc-c++  epel-release llvm5.0 llvm5.0-devel clang libicu-devel perl-ExtUtils-Embed readline readline-devel zlib zlib-devel openssl openssl-devel pam-devel libxml2-devel libxslt-devel openldap-devel systemd-devel tcl-devel python-devel

1.10 配置 sudo 权限

echo "postgres ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers > /dev/null
cat /etc/sudoers

2. 主库配置

2.1 初始化主库

initdb  -E UTF8 --locale=zh_CN.UTF-8

2.2 配置远程可访问

cat >> $PGDATA/pg_hba.conf <<EOF
host all all 0.0.0.0/0 md5
host replication replicator 192.168.182.146/24 md5 
host replication replicator 192.168.182.147/24 md5 
host replication replicator 192.168.182.148/24 md5 
EOF

2.3 配置参数文件

cat >> $PGDATA/postgresql.conf <<EOF
archive_mode = on # 启用 WAL 日志归档
archive_command = '/pgsql/pghome/arch.sh %f %p'
archive_timeout = '1800s'
wal_level = 'replica' #默认为 replica 支持流复制,logical 用于逻辑复制(发布订阅)
hot_standby = 'on'  #启用热备功能,允许从服务器在同步过程中接受查询请求。
max_wal_senders = 50 # 设置最大并发 WAL 发送进程数,决定最多能有多少个从库连接进行流复制
wal_sender_timeout = 60s #流复制主机发送数据的超时时间
wal_receiver_status_interval = 10s # 控制 walreceiver 进程向主服务器发送心跳消息的时间间隔
hot_standby_feedback = on #控制备用服务器是否会向主服务器发送关于自己的复制状态和进度的信息
wal_log_hints = on
max_replication_slots = 10 
listen_addresses = '0.0.0.0'
max_connections = 2000 
max_worker_processes = 8 #实例可用的最大后台工作进程数
wal_keep_size = 16GB  #指定 WAL 保留的目标大小,
restore_command = 'cp /data/pgwal/archive_wals/%f %p'
recovery_target_timeline = 'latest' # 主备切换后让备库自动切换到新主的时间线
EOF

归档脚本:

cat << 'EOF' > /pgsql/pghome/arch.sh
#!/bin/sh
test ! -f /data/pgwal/archive_wals/$1 && cp --preserve=timestamps $2 /data/pgwal/archive_wals/$1 ; find /data/pgwal/archive_wals/ -type f -mtime +7 -exec rm -f {} \;
EOF
chmod a+x /pgsql/pghome/arch.sh

2.4 启动数据库

pg_ctl start -l $PGHOME/logfile

2.5 创建复制槽

   standby 端如果长时间停机,重启后可能因缺少相应的 WAL 日志无法连接 Primary。此时可以通过启用 max_replication_slots 参数启用 复制槽来解决此问题。

   primary 端实例会一直保留预写日志(WAL)文件,直到所有备库所需的插槽都确认已接收到特定段为止。只有完成此操作后,主库实例才会移除相应的WAL文件。

  主库端复制槽要和从库数量对应,我这里有2个从库,因此创建2个复制槽:

select * from pg_create_physical_replication_slot('pgsql_db2');
select * from pg_create_physical_replication_slot('pgsql_db3');
--查看wal日志列表
select * from pg_ls_waldir() order by modification asc;
checkpoint;
select pg_switch_wal();
select * from pg_ls_waldir() order by modification asc;
select name, setting from pg_settings 
where name in ('wal_level','archive_mode','archive_command','archive_timeout');
show archive_mode;
show data_directory;

2.6 创建复制用户

psql -c "alter user postgres with password '123';"
psql -c "create user replicator replication login encrypted password 'replica';"

3. 配置备库

以下操作,分别在两个从节点执行。

3.1 拷贝拉平数据

pg_basebackup -D $PGDATA -Fp -Xs -v -P  -h pgsql1 -U replicator

3.2 配置参数文件

cat >> $PGDATA/postgresql.auto.conf <<EOF           
primary_conninfo='application_name=pgsql3 host=pgsql1 port=5432 user=replicator password=replica'            
max_standby_streaming_delay = 30s
wal_receiver_status_interval = 10s
hot_standby_feedback=on
primary_slot_name = 'pgsql_db3'
recovery_target_timeline='latest'
EOF

在另一个节点,记得将 pgsql3 替换成 pgsql2 。

3.3 touch 备份标识文件

cat >> $PGDATA/standby.signal <<EOF
standby_mode = 'on'
EOF

3.4 启动服务

pg_ctl start -l $PGHOME/logfile

3.5 查看同步状态

select * from pg_stat_replication;
select * from pg_stat_wal_receiver;
-- 查看复制槽
SELECT slot_name, slot_type, active, restart_lsn FROM pg_replication_slots; 
-- 查看同步lag
select pg_wal_lsn_diff(pg_current_wal_lsn(),replay_lsn) from pg_stat_replication;
-- 查看同步状态
select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;

4. 配置同步模式

在 PostgreSQL(及 openGauss、 GaussDB 等 PG 系数据库)的一主两从架构中, synchronous_standby_names 参数支持配置以下几种主要的同步模式与语法:

  1. 裸列表: 两个从库中永远只有 1 个在同步等待,另一个只是候补。
  • synchronous_standby_names = 'pgsql2,pgsql3'; 等价于 FIRST 1 (pgsql2, pgsql3);
  • 只选 1 个同步备,按列表顺序: pgsql2 是 sync, pgsql3 是 potential;
  • pgsql2 挂了, pgsql3 自动顶上变成 sync。
  1. 优先级模式(FIRST): 认顺序,不认“谁快”, pgsql2 响应再慢也得等它。
  • FIRST 1:等列表里排在最前且存活的那 1 个;
  • FIRST 2:必须 pgsql2 和 pgsql3 都确认 WAL 落盘才能 commit;任一挂掉 → 主库写阻塞(除非 synchronous_commit 降级)。
  1. 仲裁模式(ANY): 不认顺序,认“数量”,容错性比 FIRST 好。
  • ANY 1: pgsql2、 pgsql3 谁先回 ACK 算谁,主库只要拿到 1 份确认就 commit;挂掉 1 个从库不影响写入。 生产一主两从最常用。
  • ANY 2:必须两从都确认,和 FIRST 2 在 “两从都在” 时行为一样,但内部状态显示为 quorum 而非 sync/ potential。

4.1 双备库同步复制

将 两个备库的都配置同步备库: synchronous_standby_names = 'pgsql2,pgsql3';

postgres=# ALTER SYSTEM SET synchronous_standby_names = 'pgsql2,pgsql3';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)
postgres=# select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;
  pid  | application_name |  state   |   client_addr   | sync_priority | sync_state 
-------+------------------+----------+-----------------+---------------+------------
 15774 | pgsql2           | streaming| 192.168.182.147 |             1 | sync
 15740 | pgsql3           | streaming| 192.168.182.148 |             2 | potential
(2 rows)
  • 等价于   FIRST 1 (pgsql2, pgsql3),只要求 1 个备库( pgsql2)确认 WAL 写入;
  • pgsql2 存活, pgsql2 是同步备库; pgsql2 挂了, pgsql3 自动成为同步备库。
  • pgsql2、 pgsql3 都正常,只有 pgsql2 是 sync, pgsql3 是 potential。 potential 表示它目前是潜在的同步备机,在当前的同步备机不可用时可能会被启用。

4.2 单备库同步复制

postgres=# ALTER SYSTEM SET synchronous_standby_names = 'pgsql2';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)
postgres=# SHOW synchronous_standby_names;
 synchronous_standby_names ---------------------------
 pgsql2
(1 row)
postgres=# select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;
  pid  | application_name |  state   |   client_addr   | sync_priority | sync_state 
-------+------------------+----------+-----------------+---------------+------------
 15889 | pgsql2           | streaming| 192.168.182.147 |             1 | sync
 15890 | pgsql3           | streaming| 192.168.182.148 |             0 | async
(2 rows)
  • 只配置 pgsql2 为同步备库( sync),那么 pgsql3 就是异步备库( async)。
  • 主库事务提交时, 必须等待 pgsql2 确认 WAL 落盘后才返回成功。 pgsql2 故障,写操作阻塞。
  • pgsql3仍在流复制(streaming),但主库提交 不等待它的确认。 pgsql3故障,写操作不受影响。

4.3 优先级模式(FIRST)

4.3.1 要求第一个从库应答(FIRST 1)

postgres=# ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (pgsql2,pgsql3)';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)
postgres=# select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;
  pid  | application_name |  state   |   client_addr   | sync_priority | sync_state 
-------+------------------+----------+-----------------+---------------+------------
 15889 | pgsql2           | streaming| 192.168.182.147 |             1 | sync
 15890 | pgsql3           | streaming| 192.168.182.148 |             2 | potential
(2 rows)

   等价于 4.1 双备库同步复制。 按备库在列表中的出现顺序确定优先级(靠前优先),主库提交时只等待被选中的前 num_sync 个已连接备库的确认;其余不在“前 num_sync” 里的备库为 potential(潜在同步)。

4.3.2 要求前面两个从库都应答(FIRST 2)

postgres=# ALTER SYSTEM SET synchronous_standby_names = 'FIRST 2(pgsql2,pgsql3)';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)
postgres=# select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;
  pid  | application_name |  state   |   client_addr   | sync_priority | sync_state 
-------+------------------+----------+-----------------+---------------+------------
 23007 | pgsql3           | streaming| 192.168.182.148 |             2 | sync
 23034 | pgsql2           | streaming| 192.168.182.147 |             1 | sync
(2 rows)
  • pgsql2、 pgsql3 两个备库都是 sync, 但 pgsql2 的 priority=1, pgsql3 的 priority=2,越小优先级越高。
  • 挂掉任意一个从库,主库写入将会阻塞。
  • 没进列表的备库 sync_priority = 0(即 async)。

4.4 仲裁模式(基于 Quorum)

  基于法定人数( Quorum)的同步机制。只要配置的从库列表中有 任意(ANY) num_sync 个从库完成日志应答,事务即可提交,而不强制要求特定的某一个节点。

  被选中的备库在 pg_stat_replication 中显示为 quorum,不在列表中的为 async。 该策略不强调“谁先谁后”,只关心“达到数量即可”。

4.3.1 要求 2 个备库应答

postgres=# ALTER SYSTEM SET synchronous_standby_names = 'ANY 2 (pgsql2,pgsql3)';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)
postgres=# select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;
  pid  | application_name |   state   |    client_addr    | sync_priority | sync_state 
-------+------------------+-----------+-------------------+---------------+------------
 15889 | pgsql2           | streaming | 192.168.182.147   |             1 | quorum
 1590  | pgsql3           | streaming | 192.168.182.148   |             1 | quorum
(2 rows)
  • 两个备库都必须确认!
  • pgsql2、 pgsql3 都正常,主库需要等两个都确认才能提交;
  • 任一备库挂掉,主库写阻塞;

4.3.2 要求 1 个备库应答

postgres=# ALTER SYSTEM SET synchronous_standby_names = 'ANY 1 (pgsql2,pgsql3)';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
 pg_reload_conf 
----------------
 t
(1 row)
postgres=# select pid,application_name,state,client_addr,sync_priority,sync_state from pg_stat_replication;
  pid  | application_name |   state   |    client_addr    | sync_priority | sync_state 
-------+------------------+-----------+-------------------+---------------+------------
 15889 | pgsql2           | streaming | 192.168.182.147   |             1 | quorum
 1590  | pgsql3           | streaming | 192.168.182.148   |             1 | quorum
  • 只等最快的那个备库响应。谁先回 ACK 算谁,主库拿到 1 份确认就提交 。
  • 挂掉任意一个从库,主库仍能正常写入,且不丢已提交数据。
  • ANY 模式下 sync_priority 永远全 1;
  • 生产环境推荐,也是 GaussDB 一主两从的默认配置。

5. 激活备库

5.1 关闭主库,模拟故障

pg_ctl stop

5.2 将 pgsql2 激活为主库

pg_ctl promote

现在主库可读写,后台进程中不再有 startup recovering进程了,同时多了 postgres: walwriter 写进程, $PGDATA/standby.signal文件自动消失。

5.3 恢复流复制

5.3.1 创建复制槽

select * from pg_create_physical_replication_slot('pgsql_db1');
select * from pg_create_physical_replication_slot('pgsql_db3');

5.3.2 配置同步

ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (pgsql1,pgsql2,pgsql3)';
SELECT pg_reload_conf();

5.3.3 pgsql3 更改主

primary_conninfo='application_name=pgsql3 host=pgsql2 port=5432 user=replicator password=replica'

6. 总结

  本文详细记录了 PostgreSQL 一主两从架构的搭建过程,并深入解析了 synchronous_standby_names 参数所支持的多种同步复制模式及其适用场景。不过,当前方案仅实现了数据同步,尚不具备自动故障转移能力。下一篇文章我们引入 ETCD + Patroni,为 PostgreSQL 一主两从集群补齐高可用能力,也为后续深入学习 GaussDB 打下基础。

  这里是《实战派K8S&DB》,更多干货敬请关注公众号。如果本文对你有所帮助,欢迎点赞、推荐和转发,也欢迎关注后续文章,一起考证、一起学习数据库技术。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31444449/viewspace-3182018/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-02-12

  • 博文量
    25
  • 访问量
    20662