概述:
物理复制,也称为流复制,通过流复制技术可以从实例级复制出一个与主库模一样的从库。流复制通常有两种方式,同步、异步两种,主备库间延迟时间能控制在毫秒级。
逻辑复制,也称为选择性复制,基于表级别的复制,选择需要逻辑复制的表。从postgesql10 开始支持内置的逻辑复制。
WAL(Write-Ahead-Logging)日志记录数据库的变化,格式为二进制格式,当主机出现异常断电时如果WAL文件已经写入成功,但还没有来得级刷新数据文件,当数据库再次启动时会根据WAL日志文件信息进行事务前滚,从而恢复数据库到一致性状态。
尽管流复制和逻辑复制都是基于WAL,但两者有本质的不同,流复制是基于WAL物理复制,逻辑复制是基于WAL逻辑解析,将WAL解析成一种清晰、易于理解的格式。
流复制与逻辑复制的差异:
流复制 | 逻辑复制 |
实例级 | 表级 |
可对表DD操作复制 | DDL操作不会复制到备库 |
主库可读写,从库只读 | 主从库都可读写 |
PostgreSQL大版本必须一致 | 支持跨PostgreSQL大版本 |
原理:主库将预写日志WAL日志流发磅给备库,备库接收到WAL日志流后进行重做,因此复制是基于WAL日志文件的物理复制。 | 原理:也是基于WAL,逻辑复制会根据预先设置好的规则解析WAL日志,将WAL二进制文件解析成一定格式的逻辑变化信息。 |
1.异步流复制
异步流复制是指主库上提交事务时不需要等待备库接收WAL日志流并写入到备库WAL日志文件时便返回成功,而同步流复制相反。
这一小节内容主要介绍PostgreSQL异步流复制的部署,异步流复制部署主要有两种方式,一种试是拷贝数据文件方式,另一种方式是通过pg_basebackup命令行工具,这两种方式的绝大部分部署步骤都一样,只是数据复制方式不同。
流复制试验环境:
主机 | 主机名 | IP地址 | 操作系统 | PosgreSQL版本 |
主节点 | pg01 | 192.168.150.161 | CentOS Linux release 7.6.1810 (Core) | (PostgreSQL) 12.8 |
备节点 | pg02 | 192.168.150.162 | CentOS Linux release 8.4.2105 | (PostgreSQL) 12.8 |
备节点 | pg03 | 192.168.150.163 | CentOS Linux release 8.4.2105 | (PostgreSQL) 12.8 |
第一种方式:以拷贝数据文件方式部署流复制
PostgreSQL安装:请看文章《CentOS8.4 源码安装PostgreSQL12.8》https://www.modb.pro/db/125909
初始化数据实例:请看文章《PostgreSQL12.8 创建数据库实例,并启动停止服务的三种方法》https://www.modb.pro/db/129030
初始化后配置流复制相关的参数:
wal_level = replica # minimal, replica, or logical
archive_mode = on # enables archiving; off, on, or always
archive_command = 'cp "%p" "/pgdata02/12/archive/%f"' # command to use to archive a logfile segment
max_wal_senders = 10 # max number of walsender processes
wal_keep_segments = 512 # in logfile segments; 0 disables
hot_standby = on # "off" disallows queries during recovery
以上几个postgresql.conf参数是流复制的主要参数:
wal_level参数控制WAL日志输出级别,有minimal replica logical三种模式。调整此参数需要重启数据库生效,开启流复制至少需要为replica级别。
archive_mode参数控制是否启用归档,off表示不记用归档,on表示启用归档并使用archive_command参数的配置命令将WAL日志归档到归档存储上,此参数设置需重启数据库生效,通常设置成on。
archive_command参数设置WAL归档命令,可以将WAL归档到本机目录,也可以归档到远程主机上。
max_wal_senders参数控制主库上的最大WAL发送进程数,通过pg_basebackup命令在主库上做基准备份时也会消耗WAL进程,此参数设置不能比max_connection参数值高,默认为10,一个流复制备库通常只需要消耗流复制主库一个WAL发送进程。
wal_keep_segments参数设置主库pg_wal目录保留的最小WAL日志文件数,以便备库落后主库时可以通过主库保留的WAL进行追回,这个参数设置的越大,理论上备库在异常断开时追平主库的机率越大,如果归档存储空间充足,建议将此参数配置得大些,由于默认情况下每个WAL文件为16MB(编译时可通过--with-wal-segsize参数设置WAL文件大小),因此pg_wal目录大概占用空间为wal_keep_segments参数值*16MB,这里为512*16MB=8GB,实际情况下pg_wal目录下的WAL文件数会比此参数的值稍大些。
hot_standby参数控制数据库恢复过程中是否启用读操作,这个参数通常用在流复制备库,开启此参数后流复制备库支持只读SQL,但备库不支持写操作,主库和备库的postgresql.conf配置建议完全一致。
使用超级用户创建专门的流复制用户repuser,流复制用户需要有replication权限和login权限,如下所示:
postgres@[local]:1931=#create user repuser
postgres-# replication
postgres-# login
postgres-# connection limit 5
postgres-# encrypted password 'pg12ps345';
CREATE ROLE
以上完成主库的配置,接下热备生成一个备库,制作备库过程中主库仍然可读写,不影响主库上的业务,以postgres超级用户执行以下命令:
postgres@[local]:1931=#select pg_start_backup('pg12_bk1');
pg_start_backup
-----------------
0/E000028
(1 row)
pg_start_backup()函数在主库上发起一个在线备份,命令执行成功后,将数据文件拷贝到备节点pg02,pg03,如下所示:
tar czvf data.tar.gz data --exclude=data/pg_wal
scp data.tar.gz 192.168.150.163:/
tar -cvf pg_wal.tar pg_wal
pg_wal目录不是必须复制的,如果pg_wal目录下文件比较多,压缩包时可以排除这个目录,以节省数据拷贝时间,数据拷贝到备节点后,备节点的pg_wal目录需要手工创建,如果有另外的表空间目录也需要拷贝。
之后在pg02,pg03解压文件,如下所示:
[root@pg03 12]# tar -xvf data.tar.gz
文件拷贝到备节点后,在主库上执行以下命令,停止在线备份状态:
postgres@[local]:1931=#select pg_stop_backup();
NOTICE: all required WAL segments have been archived
pg_stop_backup
----------------
0/3000170
(1 row)
以上命令表示完成在线备份,在备库上配置以下参数:
recovery_target_timeline='latest'
standby_mode=on
primary_conninfo='host=192.168.150.161 port=1931 user=repuser password=123456'
特别重要的是:在备库数据目录下使用系统命令touch生成文件standby.signal表示它是备库,如果无此文件,表示是主库。参考书籍是postgresql 10 的,pg 10 使用recovery.conf文件表示备库,pg12后,取消recovery.conf文件,将参数合并到postgresql.conf文件中。
To set up the standby server, restore the base backup taken from primary server (see Section 25.3.4). Create a file standby.signal in the standby's cluster data directory. Set restore_command to a simple command to copy files from the WAL archive. If you plan to have multiple standby servers for high availability purposes, make sure that recovery_target_timeline is set to latest (the default), to make the standby server follow the timeline change that occurs at failover to another standby.
启动备库:
[postgres@pg03 data]$ pg_ctl start
。。。
done
server started
成功后,在主库查看备库信息:
postgres@[local]:1931=#\x
Expanded display is on.
postgres@[local]:1931=#select * from pg_stat_replication;
第二种方式:以pg_basebackup方式部署流复制
停止备库,删除备库。
pg_ctl stop
rm -rf /pgdata02/12/data
首先在pg02上执行一个基础备份:
[postgres@pg02 12]$ pg_basebackup -D /pgdata02/12/data -Fp -Xs -v -P -h 192.168.150.161 -p 1931 -U repuser
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/10000028 on timeline 1
pg_basebackup: starting background WAL receiver
pg_basebackup: created temporary replication slot "pg_basebackup_11649"
24598/24598 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/10000100
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: syncing data to disk ...
pg_basebackup: base backup completed
从上看,首先做了一次checkpoint,之后基于时间点做了一个全库备份。
之后需要使用系统命令生成代表备库的文件standby.signa,同时备primary_conninfo参数。
启动备库:
[postgres@pg02 data]$ pg_ctl start
。。。
done
server started
在主库查看备库信息:
postgres@[local]:1931=#select * from pg_stat_replication;
2.同步流复制
在异步流复制的基础上,进行配置:
第一步,配置主库:
synchronous_standby_names = 'pg02,pg03'
第二步:配置备库:
primary_conninfo = 'application_name=pg02 host=192.168.150.161 port=1931 user=repuser password=123456'
primary_conninfo = 'application_name=pg03 host=192.168.150.161 port=1931 user=repuser password=123456'
第三步:重启备库,重新加载主库:
pg_ctl restart
pg_ctl reload
第四步:查看主库库同步状态:
postgres@[local]:1931=#select application_name,client_addr,sync_priority,sync_state from pg_stat_replication;
application_name | client_addr | sync_priority | sync_state
------------------+-----------------+---------------+------------
pg02 | 192.168.150.162 | 1 | sync
pg03 | 192.168.150.163 | 2 | potential
从上可以看到备库,pg02是同步状态“sync",pg03状态是”potential",表示是潜在的同步库。
测试同步状态变化:
停掉pg02后,pg03自动变成同步状态,如下所示:
[postgres@pg02 data]$ pg_ctl stop
done
server stopped
postgres@[local]:1931=#select application_name,client_addr,sync_priority,sync_state from pg_stat_replication;
application_name | client_addr | sync_priority | sync_state
------------------+-----------------+---------------+------------
pg03 | 192.168.150.163 | 2 | sync
再次启动pg02,pg02成为同步状态,pg03成为备用同步状态。
[postgres@pg02 data]$ pg_ctl start
...
done
server started
postgres@[local]:1931=#select application_name,client_addr,sync_priority,sync_state from pg_stat_replication;
application_name | client_addr | sync_priority | sync_state
------------------+-----------------+---------------+------------
pg02 | 192.168.150.162 | 1 | sync
pg03 | 192.168.150.163 | 2 | potential
(2 rows)
将库配置成两个同步节点:
synchronous_standby_names = '2(pg02,pg03)' # standby servers that provide sync rep
重新加载主库:
[postgres@pg01 data]$ pg_ctl reload
server signaled
。。。
查看备库同步状态,成为两个同步:
postgres@[local]:1931=#select application_name,client_addr,sync_priority,sync_state from pg_stat_replication;
application_name | client_addr | sync_priority | sync_state
------------------+-----------------+---------------+------------
pg02 | 192.168.150.162 | 1 | sync
pg03 | 192.168.150.163 | 2 | sync
(2 rows)
测试当一个备库停止后,主库是事可以写入:
备库:
[postgres@pg02 data]$ pg_ctl stop
。。。
done
server stopped
主库写入Hang住:
postgres@[local]:1931=#insert into test_sr values (6);
这时启动备库:
[postgres@pg02 data]$ pg_ctl start
。。。
done
server started
主库:
postgres@[local]:1931=#insert into test_sr values (6);
2021-10-18 11:13:31.449 CST [13722] LOG: standby "pg02" is now a synchronous standby with priority 1
2021-10-18 11:13:31.449 CST [13722] STATEMENT: START_REPLICATION 0/14000000 TIMELINE 1
INSERT 0 1
备库验证是否写入数据:
postgres=# select * from test_sr;
id
----
2
5
6
(3 rows)
配置quorum模式的两个同步节点的情况:
将主库参数synchronous_standby_names进行如下配置,重新加载reload生效:
synchronous_standby_names = 'any 2(pg02,pg03,pg04)' # standby servers that provide sync rep
[postgres@pg01 data]$ pg_ctl reload
server signaled
查看同步状态,发现各个节点优先级都一样,同步状态都变成了quorum,这时停掉一个备库,主库还是正常写入:
postgres@[local]:1931=#select application_name,client_addr,sync_priority,sync_state from pg_stat_replication;
application_name | client_addr | sync_priority | sync_state
------------------+-----------------+---------------+------------
pg04 | 192.168.150.164 | 1 | quorum
pg02 | 192.168.150.162 | 1 | quorum
pg03 | 192.168.150.163 | 1 | quorum
(3 rows)
至此,流复制的同步与异步,配置完成,初学不到之处多,请喷。




