PostgreSQL 一主两从搭建及同步模式深度详解 本打算介绍一下
GaussDB 集中式集群的同步机制,但考虑到其底层本质上仍基于
PostgreSQL 流复制,因此先“回归本源”, 介绍
PostgreSQL 一主两从集群的搭建过程,并详细解析
synchronous_standby_names 参数配置为
ANY、
FIRST 和
* 场景下复制模式的差异,为后面介绍
GaussDB 复制技术奠定基础。
| 主机名 | IP地址 | 角色作用 |
|---|---|---|
| pgsql1 | 192.168.182.146 | 主库 |
| pgsql2 | 192.168.182.147 | 从库1 |
| pgsql3 | 192.168.182.148 | 从库2 |
hostnamectl set-hostname pgsql1 hostnamectl set-hostname pgsql2 hostnamectl set-hostname pgsql3
hosts 文件cat >> /etc/hosts <<EOF 192.168.182.146 pgsql1 192.168.182.147 pgsql2 192.168.182.148 pgsql3 EOF
SELINUXsystemctl status firewalld systemctl stop firewalld systemctl disable firewalld systemctl status firewalld sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
useradd postgres echo 'postgres' | passwd --stdin postgres
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
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}
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
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
echo "postgres ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers > /dev/null cat /etc/sudoers
initdb -E UTF8 --locale=zh_CN.UTF-8
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
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
pg_ctl start -l $PGHOME/logfile
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;
psql -c "alter user postgres with password '123';" psql -c "create user replicator replication login encrypted password 'replica';"
以下操作,分别在两个从节点执行。
pg_basebackup -D $PGDATA -Fp -Xs -v -P -h pgsql1 -U replicator
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 。
cat >> $PGDATA/standby.signal <<EOF standby_mode = 'on' EOF
pg_ctl start -l $PGHOME/logfile
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;
在
PostgreSQL(及
openGauss、
GaussDB 等 PG 系数据库)的一主两从架构中,
synchronous_standby_names 参数支持配置以下几种主要的同步模式与语法:
synchronous_standby_names = 'pgsql2,pgsql3'; 等价于
FIRST 1 (pgsql2, pgsql3);pgsql2 是
sync,
pgsql3 是
potential;pgsql2 挂了,
pgsql3 自动顶上变成 sync。pgsql2 响应再慢也得等它。FIRST 1:等列表里排在最前且存活的那 1 个;FIRST 2:必须
pgsql2 和
pgsql3 都确认 WAL 落盘才能 commit;任一挂掉 → 主库写阻塞(除非 synchronous_commit 降级)。ANY 1:
pgsql2、
pgsql3 谁先回 ACK 算谁,主库只要拿到 1 份确认就
commit;挂掉 1 个从库不影响写入。
生产一主两从最常用。ANY 2:必须两从都确认,和
FIRST 2 在 “两从都在” 时行为一样,但内部状态显示为
quorum 而非
sync/
potential。将 两个备库的都配置同步备库:
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 表示它目前是潜在的同步备机,在当前的同步备机不可用时可能会被启用。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故障,写操作不受影响。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(潜在同步)。
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)。Quorum) 基于法定人数(
Quorum)的同步机制。只要配置的从库列表中有
任意(ANY)
num_sync 个从库完成日志应答,事务即可提交,而不强制要求特定的某一个节点。
被选中的备库在
pg_stat_replication 中显示为
quorum,不在列表中的为
async。
该策略不强调“谁先谁后”,只关心“达到数量即可”。
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 都正常,主库需要等两个都确认才能提交;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
sync_priority 永远全 1;GaussDB 一主两从的默认配置。pg_ctl stop
pg_ctl promote
现在主库可读写,后台进程中不再有
startup recovering进程了,同时多了
postgres: walwriter 写进程,
$PGDATA/standby.signal文件自动消失。
select * from pg_create_physical_replication_slot('pgsql_db1');
select * from pg_create_physical_replication_slot('pgsql_db3');
ALTER SYSTEM SET synchronous_standby_names = 'FIRST 1 (pgsql1,pgsql2,pgsql3)'; SELECT pg_reload_conf();
primary_conninfo='application_name=pgsql3 host=pgsql2 port=5432 user=replicator password=replica'
本文详细记录了
PostgreSQL 一主两从架构的搭建过程,并深入解析了
synchronous_standby_names 参数所支持的多种同步复制模式及其适用场景。不过,当前方案仅实现了数据同步,尚不具备自动故障转移能力。下一篇文章我们引入
ETCD + Patroni,为
PostgreSQL 一主两从集群补齐高可用能力,也为后续深入学习
GaussDB 打下基础。
这里是《实战派K8S&DB》,更多干货敬请关注公众号。如果本文对你有所帮助,欢迎点赞、推荐和转发,也欢迎关注后续文章,一起考证、一起学习数据库技术。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/31444449/viewspace-3182018/ ,如需转载,请注明出处,否则将追究法律责任。