本文记录了如何在RHEL 7.6下搭建PostgreSQL 12.2的一主两备高可用集群。
一、方案比较
官网共列了8种方案,各有优缺点,详细可参考:
https://www.postgresql.org/docs/current/different-replication-solutions.html

图1.高可用方案比较
综合比较后,本文将选用流复制的同步方式搭建一主两备的高可用集群,主要优势有:主备强一致性、数据永不丢失、原生备份方式、轻量、读性能佳;主要缺点为只能主机执行写,且同步模式下需等待备机写完。因此,想要做读写分离,或者对于读性能要求高、写请求性能要求不那么高的场景,该方案是比较合适的。
流复制的主要原理
流复制是搭建主备库的一种有效方式,它不需要额外增加软件,只需要在单数据库模式的基础上,再复制一份PostgreSQL数据库到另外的一台机器上,对两台数据库进行参数配置,即可实现。
这两套数据库之间的数据,通过wal日志,后台自动同步。对外部的应用程序而言,可以看作是两套数据库,需要根据业务需要,显式分别连接不同的数据库。
PG主备流复制的核心部分由walsender,walreceiver和startup三个进程组成。
walsender进程是用来发送WAL日志记录的,执行顺序如下:
PostgresMain()->exec_replication_command()->StartReplication()->WalSndLoop()->XLogSendPhysical()
walreceiver进程是用来接收WAL日志记录的,执行顺序如下:
sigusr1_handler()->StartWalReceiver()->AuxiliaryProcessMain()->WalReceiverMain()->walrcv_receive()
startup进程是用来apply日志的,执行顺序如下:
PostmasterMain()->StartupDataBase()->AuxiliaryProcessMain()->StartupProcessMain()->StartupXLOG()
下图是PG主备总体框架图:

图2.PG主备总体框架图
walsender和walreceiver进程流复制过程
walsender和walreceiver交互主要分为以下几个步骤:
1、walreceiver启动后通过recovery.conf文件中的primary_conninfo参数信息连向主库,主库通过连接参数replication=true启动walsender进程;
2、walreceiver执行identify_system命令,获取主库systemid/timeline/xlogpos等信息,执行TIMELINE_HISTORY命令拉取history文件;
3、执行wal_startstreaming开始启动流复制,通过walrcv_receive获取WAL日志,期间也会回应主库发过来的心跳信息(接收位点、flush位点、apply位点),向主库发送feedback信息(最老的事务id),避免vacuum删掉备库正在使用的记录;
4、执行walrcv_endstreaming结束流复制,等待startup进程更新receiveStart和receiveStartTLI,一旦更新,重新进入2/3/4步骤。
二、环境准备
#准备3台RHEL7.6的虚机
角色 | IP | 端口 | 数据库用户名/密码 | 流复制用户名/密码 | 主机名 |
主库 | 192.168.0.145 | 5432 | postgres/postgres | repl/ postgres | pgmaster |
备库 | 192.168.0.146 | 5432 | postgres/postgres | repl/ postgres | pgslave01 |
备库 | 192.168.0.147 | 5432 | postgres/postgres | repl/ postgres | pgslave02 |
#防火墙设置
firewall-cmd --zone=public --add-port=5432/tcp
#关闭selinux
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g etc/selinux/config
setenforce 0
# 配置阿里云yum源
mv etc/yum.repos.d/CentOS-Base.repo etc/yum.repos.d/CentOS-Base.repo.bak
curl -o etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
#安装NTP
yum -y install ntpntpdate
systemctl enable ntpd
vi etc/ntp.conf
# 添加ntp服务器地址
server ntp1.aliyun.comprefer
server ntp2.aliyun.com
server ntp3.aliyun.com
service ntpd stop(关闭服务)
ntpdate ntp1.aliyun.com(手动更新一次)
sevice ntpd start(开启服务)
三、安装PostgreSQL
官方下载地址:
https://www.postgresql.org/download/
#Install the repositoryRPM:
yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
#Install the clientpackages:
yum install postgresql12
#Optionally install theserver packages:
yum install postgresql12-server
#若要内网环境安装,可通过yum下载rpm包
yum install postgresql12 postgresql12-server --downloadonly--downloaddir=~/
#rpm安装顺序
1、postgresql12-libs-12.2-1PGDG.rhel7.x86_64
2、postgresql12-12.2-1PGDG.rhel7.x86_64 #客户端
3、postgresql12-server-12.2-1PGDG.rhel7.x86_64 #服务端
#rpm安装
rpm -ivhpostgresql12-libs-12.2-1PGDG.rhel7.x86_64
\postgresql12-12.2-1PGDG.rhel7.x86_64
\postgresql12-server-12.2-1PGDG.rhel7.x86_64
#初始化数据库
/usr/pgsql-12/bin/postgresql-12-setupinitdb
systemctl enable postgresql-12
systemctl start postgresql-12
#修改postgreq用户的密码
passwd postgres
更改用户postgres 的密码。
新的密码:
无效的密码:密码是一个回文
重新输入新的密码:
passwd:所有的身份验证令牌已经成功更新。
su - postgres
-bash-4.2$ psql
psql (12.1)
输入"help" 来获取帮助信息.
postgres=# ALTER USERpostgres with password 'postgres';
ALTER ROLE
postgres=# \q
-bash-4.2$ exit
登出
#修改数据库访问权限
vi/var/lib/pgsql/12/data/pg_hba.conf
......
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unixdomain socket connections only
local all all md5
# IPv4 local connections:
host all all 127.0.0.1/32 md5
host all all 0.0.0.0/0 md5
# IPv6 local connections:
host all all ::1/128 md5
# Allow replication connections fromlocalhost, by a user with the
# replication privilege.
#local replication all peer
#host replication all 127.0.0.1/32 ident
#host replication all ::1/128 ident
host replication repl 192.168.0.145/30 trust
#允许远程访问数据库
vi var/lib/pgsql/12/data/postgresql.conf
# - Connection Settings-
listen_addresses ='*' # what IPaddress(es) to listen on;
# comma-separated list of addresses;
#defaults to 'localhost'; use '*' for all
#(change requires restart)
port = 5432 # (change requiresrestart)
#重启数据库
systemctl restart postgresql-12
#设置环境变量
cat <<EOF >/etc/profile.d/pgsql.sh
POSTGRESQL12_HOME=/usr/pgsql-12
PATH=\$POSTGRESQL12_HOME/bin:\$PATH
export POSTGRESQL12_HOMEPATH
EOF
source etc/profile.d/pgsql.sh
四、高可用配置
1、主库设置
su - postgres
##初始化
initdb -D/data/pgdata/data -U postgres --locale=en_US.UTF8 -E UTF8
cp /data/pgdata/data/{pg_hba.conf,pg_hba.conf.bak}
## 允许repl用户进行流复制(这步前面已完成,不需要再单独配了)
cat <<-eof>>/data/pgdata/data/pg_hba.conf
host all all 0.0.0.0/0 md5
host replication repl 192.168.0.145/30 trust
eof
cp /data/pgdata/data/{postgresql.conf,postgresql.conf.bak}
##配置postgresql.conf
cat <<-eof>/data/pgdata/data/postgresql.conf
listen_addresses ='*'
port = 5432
max_connections = 100
superuser_reserved_connections= 10
unix_socket_directories= '/tmp'
shared_buffers = 512MB
temp_buffers = 16MB
work_mem = 32MB
maintenance_work_mem =128MB
#max_stack_depth = 2MB
dynamic_shared_memory_type= posix
wal_level = replica
full_page_writes = on
wal_log_hints = off # also do full page writes ofnon-critical updates
# (change requires restart)
wal_buffers = 16MB
wal_writer_delay = 200ms
commit_delay = 0
commit_siblings = 5
archive_mode = on
archive_command = 'test! -f /var/lib/pgsql/12/archivedir/%f && cp %p/var/lib/pgsql/12/archivedir/%f'
archive_timeout = 60s
max_wal_senders = 50
synchronous_standby_names = 'pgslave01, pgslave02' #同步流复制的备机名字
hot_standby = on
effective_cache_size =2GB
log_destination ='csvlog'
logging_collector = on
log_directory = 'log'
log_filename ='postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = 1d
log_rotation_size = 10MB
log_statement = 'mod'
log_timezone ='Asia/Shanghai' #PRC也行
timezone ='Asia/Shanghai'
eof
## 启动数据库
pg_ctl start -D /var/lib/pgsql/12/data/ -l/var/lib/pgsql/12/data/serverlog
## 创建用户进行流复制连接
psql -U postgres -c"ALTER USER postgres WITH PASSWORD 'postgres';"
psql -U postgres -c"CREATE USER repl WITH PASSWORD'postgres' REPLICATION;"
2、备库设置
以下以pgslave01为例,pgslave02同理。
su - postgres
pg_ctl stop -m fast -D /var/lib/pgsql/12/data/ #关闭数据库
sudo mv /var/lib/pgsql/12/data/* /home/pgbak #备份配置文件
## 进行一次主库数据基本备份到备库
pg_basebackup -h 192.168.0.145 -p 5432 -U repl -w -Fp -Xs -Pv -R -D /var/lib/pgsql/12/data/
pg_basebackup: 开始基础备份,等待检查点完成
pg_basebackup: 已完成检查点
pg_basebackup: 预写日志起始于时间点: 0/6000028, 基于时间轴1
pg_basebackup: 启动后台 WAL 接收进程
pg_basebackup: 已创建临时复制槽"pg_basebackup_15842"
33565/33565 kB (100%),1/1 表空间
pg_basebackup: 预写日志结束点: 0/6000100
pg_basebackup: 等待后台进程结束流操作...
pg_basebackup: 同步数据到磁盘...
pg_basebackup: 基础备份已完成
##根据主库的postgresql.conf 配置文件修改参数配置:
listen_addresses = '* '
max_connections = 150, #一般比主库大一些.
wal_level =replica
hot_standby = on
max_standby_streaming_delay= 30s #默认
wal_receiver_status_interval= 10s #默认
hot_standby_feedback =on # 如果有错误的数据复制,是否向主进行反应
primary_conninfo = 'application_name=pgslave01user=repl passfile=''/var/lib/pgsql/.pgpass'' host=192.168.0.145 port=5432sslmode=prefer sslcompression=0 gssencmode=prefer krbsrvname=postgrestarget_session_attrs=any'
## 启动备库
pg_ctl start -D /var/lib/pgsql/12/data/ -l/var/lib/pgsql/12/data/serverlog
五、验证
##状态检查
### 回到主库查看状态
$ psql -c "selectclient_addr,usename,backend_start,application_name,sync_state,sync_priorityFROM pg_stat_replication;"
client_addr | usename | backend_start | application_name | sync_state |sync_priority
---------------+---------+-------------------------------+------------------+------------+---------------
192.168.0.147 | repl | 2020-05-1016:47:23.051332+08 | pgslave02 | potential | 2
192.168.0.146 | repl | 2020-05-10 16:45:46.412336+08 |pgslave01 | sync | 1
(2 行记录)
$ psql -c "select client_addr, sync_state frompg_stat_replication;"
client_addr | sync_state
---------------+------------
192.168.0.147| potential
192.168.0.146 | sync
(2 行记录)
##备库只读, 所以任何操作, 都会告警失败:
postgres=# drop databasetest;
错误: 不能在一个只读模式的事务中执行DROP DATABASE
## 查看主备的状态
#主
ps -ef |grep sender
postgres 38662 38651 16:47? 00:00:00 postgres: walsender repl192.168.0.146(42126) streaming 0/17000060
postgres 38677 38651 16:45? 00:00:00 postgres: walsender repl192.168.0.147(42290) streaming 0/17000060
postgres 38795 38606 0 17:16 pts/0 00:00:00 grep --color=auto sender
#备
ps -ef |grep recover
postgres 16041 16039 0 16:45 ? 00:00:00 postgres: startup recovering 000000010000000000000008
root 16112 14979 0 17:15 pts/0 00:00:00 grep --color=auto recover
#通过此命令判断主备
#主
pg_controldata -D /var/lib/pgsql/12/data/
pg_control 版本: 1201
Catalog 版本: 201909212
数据库系统标识符: 6822534294356894336
数据库簇状态: 在运行中
#备
pg_controldata -D/var/lib/pgsql/12/data/
pg_control 版本: 1201
Catalog 版本: 201909212
数据库系统标识符: 6822534294356894336
数据库簇状态: 正在归档恢复




