关于更详细的内容,可以从由谭峰、张文升出版的postgresql实战一书里面的第12章查看。
PostgreSQL数据库本身提供三种HA模式,这里简单介绍:
1. 基于日志文件的复制
Master库向Standby库异步传输数据库的WAL日志,Standby解析日志并把日志中的操作重新执行,以实现replication功能。缺点在于Master库必须等待每个WAL日志填充完整后才能发给Standby,如果在填充WAL日志的过程中Master库宕机,未发送的日志内的事务操作会全部丢失。
2. 异步流复制模式
Master库以流模式向Standby库异步传输数据库的WAL日志,Standby解析收到的内容并把其中的操作重新执行,以实现replication功能。这种方式和“基于日志文件的复制”相比不需要等待整个WAL日志填充完毕,大大降低了丢失数据的风险,但在Master库事务提交后,Standby库等待流数据的时刻发生Master宕机,会导致丢失最后一个事务的数据。同时备库可以配置成HOT Standby,可以向外提供查询服务,供分担负载。
3. 流同步复制模式(Synchronous Replication)
顾名思义,是流复制模式的同步版本。向Master库发出commit命令后,该命令会被阻塞,等待对应的WAL日志流在所有被配置为同步节点的数据库上提交后,才会真正提交。因此只有Master库和Standby库同时宕机才会丢数据。多层事务嵌套时,子事务不受此保护,只有最上层事务受此保护。纯读操作和回滚不受此影响。同时备库可以配置成HOT Standby,可以向外提供查询服务,供分担负载。采用这种模式的性能损耗依据网络情况和系统繁忙程度而定,网络越差越繁忙的系统性能损耗越严重。
话不多说,动手--------------------------------------------
准备两台机器(centos7、postgresql10.6):
192.168.64.12 master
192.168.64.13 standby
先看异步流复制
在master主库上创建流复制用户(也可以是超级用户,但最好新建role)
postgres=# CREATE ROLE replicator login replication password '123456';
CREATE ROLE
postgres=# \du
List of roles
Role name | Attributes | Member of
------------+------------------------------------------------------------+-----------
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
replicator | Replication | {}
修改master库的pg_hba.conf,把master库和standby库的IP地址添加进master库网络策略白名单中,使standby库可以连上master库,同时便于主备切换。
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
host all all 192.168.64.0/24 md5
# IPv6 local connections:
host all all ::1/128 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all trust
host replication all 127.0.0.1/32 trust
host replication all ::1/128 trust
host replication replicator 192.168.64.12/32 md5
host replication replicator 192.168.64.13/32 md5
修改master库的postgresql.conf文件(注意,在原有基础上改动以下参数)
新建归档目录 mkdir /data/arch_dir /data/arch_dir_master
wal_level= logical
max_wal_senders = 10 # at least the number of standby
archive_mode = on
archive_command = 'test ! -f /data/arch_dir/%f && cp %p /data/arch_dir/%f'
synchronous_standby_names = '' #standby application name, in recover.conf
hot_standby=on
说明:
synchronous_standby_names参数对应的参数为同步复制保障节点,如果该参数非空,则任何一个最上层的事务都会等待被同步到该参数指明的节点后才会在Master库提交,如果Standby库无响应Master库会被hung住。如果该参数为空,则表示采用异步复制方式。该参数可以配置多个保障节点,以逗号分隔,PG会从第一个开始尝试。开启同步复制存虽然能最大限度保证数据安全,但是会影响应用可用性。同步模式下,如果Master和Standby之间的网络状况很糟糕,那么同步复制会极大的拉低整个系统的性能;如果Standby宕机,主库会被hang住,虽然这里只记录异步复制,但具体采用何种复制请按自身业务场景选择。
在master上创建切换为standby库时的同步配置文件recovery.done(.done文件是主库上的,不被启用,.conf是备库上的使用)
vi /data/pgdata/recovery.done
standby_mode=on
restore_command = 'cp /data/arch_dir_master/%f %p'
primary_conninfo='application_name=pg2 host=192.168.64.13 port=5432 user=replicator password=123456'
archive_cleanup_command ='pg_archivecleanup /data/arch_dir_master %r'
recovery_target_timeline = 'latest'
重启master主库。pg_ctl restart,至此主库上的操作已完成。接下来看看standby备库的。
standby库需要以master库的完整备份+归档日志恢复而来,如果master库尚未对外提供服务,也可以直接复制master库的数据文件目录,这里采用第一种方法,更贴近实际环境。
使用主库的热备创建standby库(此步骤在主库执行)
[postgres@hmcf-02 pgdata]$ psql
psql (10.6)
Type "help" for help.
postgres=# select pg_start_Backup('backuptag',true);
pg_start_backup
-----------------
0/2000060
(1 row)
postgres=#
从主库上拷贝数据目录到standby
[root@hmcf-03 data]# scp -r 192.168.64.12:/data/pgdata /data
[root@hmcf-03 data]# chown -R postgres.postgres pgdata/
停止主库的热备锁定(此步骤在主库执行)
postgres=# select pg_stop_backup();
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
pg_stop_backup
----------------
0/2000168
(1 row)
在standby上清理复制过来的主库文件
[postgres@hmcf-03 data]$ rm -rf pgdata/pg_wal
[postgres@hmcf-03 data]$ rm -rf pgdata/postmaster.pid
[postgres@hmcf-03 data]$ mkdir arch_dir
修改备库的recovery文件
[postgres@hmcf-03 pgdata]$ mv recovery.done recovery.conf
内容如下
[postgres@hmcf-03 pgdata]$ cat recovery.conf
standby_mode=on
restore_command = 'cp /PostgreSQL/10/data/arch_dir_master/%f %p'
primary_conninfo='application_name=pg1 host=192.168.64.12 port=5432 user=replicator password=123456'
archive_cleanup_command ='pg_archivecleanup /data/arch_dir_master %r'
recovery_target_timeline = 'latest'
准备恢复需要的wal和归档文件
[root@hmcf-03 data]# scp -r 192.168.64.12:/data/pgdata/pg_wal ./pgdata
[email protected]'s password:
000000010000000000000002.00000060.backup.done 100% 0 0.0KB/s 00:00
000000010000000000000002.done 100% 0 0.0KB/s 00:00
000000010000000000000002 100% 16MB 67.7MB/s 00:00
000000010000000000000002.00000060.backup 100% 293 454.4KB/s 00:00
000000010000000000000003 100% 16MB 88.5MB/s 00:00
000000010000000000000004 100% 16MB 11.0MB/s 00:01
[root@hmcf-03 data]# chown -R postgres.postgres pgdata
[root@hmcf-03 data]# scp -r 192.168.64.12:/data/arch_dir /data/arch_dir_master
[email protected]'s password:
Permission denied, please try again.
[email protected]'s password:
000000010000000000000001 100% 16MB 6.4MB/s 00:02
000000010000000000000002.00000060.backup 100% 293 229.1KB/s 00:00
000000010000000000000002
[root@hmcf-03 data]# chown -R postgres.postgres arch_dir_master
启动备库,观察备库日志
(或者是使用basebackup命令进行热备)
在备库上执行写操作测试
[postgres@hmcf-03 ~]$ psql
psql (10.6)
Type "help" for help.
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
postgres | postgres | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 |
template0 | postgres | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | zh_CN.UTF-8 | zh_CN.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)
postgres=# create table a(name text);
2019-11-23 18:15:46.078 CST [16175] ERROR: cannot execute CREATE TABLE in a read-only transaction
2019-11-23 18:15:46.078 CST [16175] STATEMENT: create table a(name text);
ERROR: cannot execute CREATE TABLE in a read-only transaction
postgres=#
再在master 上测试。
[postgres@hmcf-02 ~]$ psql
psql (10.6)
Type "help" for help.
postgres=# create table a(name text);
CREATE TABLE
postgres=#
postgres=# select * from pg_stat_replication;
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_lsn | write_lsn |
flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state
-------+----------+------------+------------------+---------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+
-----------+------------+-----------+-----------+------------+---------------+------------
17226 | 16384 | replicator | pg1 | 192.168.64.13 | | 34786 | 2019-11-23 18:14:08.190221+08 | | streaming | 0/301E9B8 | 0/301E9B8 |
0/301E9B8 | 0/301E9B8 | | | | 0 | async
(1 row)
postgres=#
可以看到,standby已经连接上来了。此时的状态是async异步流复制模式。
下面看看同步复制
同步复制只需在异步复制的基础上,修改postgresql.conf里面的synchronous_standby_names 参数,对应参数值为standby备库上recovery.conf里面的primary_conninfo中的application_name。
然后重新reload加载配置即可。
查看验证。
[postgres@hmcf-02 pgdata]$ psql
psql (10.6)
Type "help" for help.
postgres=# select * from pg_stat_replication;
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_lsn | write_lsn |
flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state
-------+----------+------------+------------------+---------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+
-----------+------------+-----------+-----------+------------+---------------+------------
17226 | 16384 | replicator | pg1 | 192.168.64.13 | | 34786 | 2019-11-23 18:14:08.190221+08 | | streaming | 0/301E9B8 | 0/301E9B8 |
0/301E9B8 | 0/301E9B8 | | | | 1 | sync
(1 row)
postgres=#
可以看到此时standby的同步状态变成了sync了,由异步流变成了同步流复制。