一、前言
PostgreSQL号称是最先进的开源数据库,对标oracle。不过从使用角度来看,个人认为pg过于学院派了,使用上比较不友好。它的设计理念还是很先进的。我今天写本文主要是从主从的角度聊一下postgreSQL。postgresql 提供了主从复制功能,有基于文件的拷贝和基于 tcp 流的数据传输两种方式。两种方式都是传输 wal 数据,前者是等待生成一个完整的wal文件后,才会触发传输,后者是实时传输的。可以看出来基于文件方式的延迟会比较高,而且wal文件有可能没来得及传输就被损坏了,造成了这块的数据丢失。基于 tcp 流的方式,延迟非常低,是现在最常见的方式,本篇文章也主要讲述这种方式。不同于基于文件的日志传送,流复制的关键在于“流”,所谓流,就是没有界限的一串数据,类似于河里的水流,是连成一片的。因此流复制允许一台后备服务器比使用基于文件的日志传送更能保持为最新的状态。比如我们有一个大文件要从本地主机发送到远程主机,如果是按照“流”接收到的话,我们可以一边接收,一边将文本流存入文件系统。这样,等到“流”接收完了,硬盘写入操作也已经完成。
PostgreSQL物理流复制按照同步方式分为两类:
- 异步流复制
- 同步流复制
物理流复制具有以下特点:
1、延迟极低,不怕大事务
2、支持断点续传
3、支持多副本
4、配置简单
5、备库与主库物理完全一致,并支持只读
二、流复制发展历史
pg在流复制出现之前,使用的就是基于文件的日志传送:对wal日志进行拷贝,因此从库始终落后主库一个日志文件,并且使用rsync工具同步data目录。而流复制出现是从2010年推出的pg9.0开始的,其历史大致为:
- 起源:pg9.0开始支持流式物理复制,用户可以通过流式复制,构建只读备库
(主备物理复制,块级别一致)。流式物理复制可以做到极低的延迟(通常在1毫秒以内)。 - 同步流复制:pg9.1开始支持同步复制,但是当时只支持一个同步流复制备节点(例如配置了3个备,只有一个是同步模式的,其他都是异步模式)。同步流复制的出现,保证了数据的0丢失。
- 级联流复制:pg9.2支持级联流复制。即备库还可以再连备库。
- 流式虚拟备库:pg9.2还支持虚拟备库,即就是只有WAL,没有数据文件的备库。
- 逻辑复制:pg9.4开始可以实现逻辑复制,逻辑复制可以做到对主库的部分复制,例如表级复制,而不是整个集群的块级一致复制。
- 增加多种同步级别:pg9.6版本开始可以通过synchronous_commit参数,来配置事务的同步级别。
三、流复制原理
3.1 流复制原理图
从上图我们可以看到流复制中日志提交的大致流程为:
1、事务commit后,日志在主库写入wal日志,还需要根据配置的日志同步级别,等待从库反馈的接收结果。
2、主库通过日志传输进程将日志块传给从库,从库接收进程收到日志开始回放,最终保证主从数据一致性。
3.2、流复制同步级别
PostgreSQL通过配置synchronous_commit (enum)参数来指定事务的同步级别。我们可以根据实际的业务需求,对不同的事务,设置不同的同步级别。
synchronous_commit = off # synchronization level;
# off, local, remote_write, or on
- remote_apply:事务commit或rollback时,等待其redo在primary、以及同步standby(s)已持久化,并且其redo在同步
standby(s)已apply。 - on:事务commit或rollback时,等待其redo在primary、以及同步standby(s)已持久化。
- remote_write:事务commit或rollback时,等待其redo在primary已持久化; 其redo在同步standby(s)已调用write接口(写到 OS, 但是还没有调用持久化接口如fsync)。
- local:事务commit或rollback时,等待其redo在primary已持久化;
- off:事务commit或rollback时,等待其redo在primary已写入wal buffer,不需要等待其持久化;
不同的事务同步级别对应的数据安全级别越高,对应的对性能影响也就越大。上述从上至下安全级别越来越低。
四、流复制配置过程
PostgreSQL物理流复制大致过程为:
1、PG软件安装
2、postgresql.conf参数配置
3、pg_hba.conf配置
4、pg_basebackup方式部署备库
5、配置简单
6、备库与主库物理完全一致,并支持只读
4.1、异步流复制参数配置
postgresql.conf :
wal_level = replica # minimal, replica, or logical
max_wal_senders = 10 wal_keep_segments = 1024 hot_standby = on
pg_hba.conf :
host replication postgres
# max number of walsender processes
# in logfile segments, 16MB each; 0 disables
192.168.7.180/32 md5
standby recovery.conf :
recovery_target_timeline = 'latest'
standby_mode = on
primary_conninfo = 'host=192.168.7.180 port=1921 user=bill password=xxx
4.2、同步流复制参数配置
postgresql.conf :
wal_level = replica # minimal, replica, or logical
max_wal_senders = 10 # max number of walsender processes wal_keep_segments = 1024 # in logfile segments, 16MB each; 0 disables hot_standby = on
synchronous_commit = remote_write、on、remote_apply synchronous_standby_names = 'standby2'
pg_hba.conf :
host replication postgres 192.168.7.180/32 md5
standby recovery.conf :
recovery_target_timeline = 'latest'
standby_mode = on
primary_conninfo = 'host=192.168.7.180 port=1921 user=bill password=xxx application_name=standby2'
另外我们可以通过设置synchronous_standby_names参数来指定一个支持同步复制的后备服务器的列表,其支持格式大致为:
1、synchronous_standby_names =standby_name [, ...] 2、synchronous_standby_names =[FIRST] num_sync ( standby_name [, ...]) 3、synchronous_standby_names =ANY num_sync ( standby_name [, ...] )
五、PostgreSQL主备数据库切换
1、识别当前库主、备角色:
方式一:
postgres=# select pg_is_in_recovery(); 结果是f则为主库,t为备库
。
方式二:
pg_controldata 结果为
cluster state
是in production则为主库;结果为cluster state是in archive recovery则为备库
。
方式三:
Select pid, application_name, client_addr, client_port, state, sync_state from pg_stat_replication 查询到结果为主库,查询不到结果为备库。
2、主备倒换
在PostgreSQL如主库出现异常时,备库如何激活。有2种方式:
方式一:使用pg_ctl promote来激活(PostgreSQL9.1后支持)
(1)关闭主库(模拟主库故障):
$ pg_ctl stop -m fast
(2)在备库上执行pg_ctl promote命令激活备库
如果recovery.conf变成recovery.done表示备库已切换成主库
(3)原主库变备库
在新备库上创建recovery.conf、.pgpass文件,内容参考前文章节。启动新备库:
$ pg_ctl start
方式二:备库在recovery.conf文件中有个配置项trigger_file,是激活standby的触发文件,通过检测这个文件是否存在,存在则激活standby为master。
(1)在recovery.conf中配置触发器文件地址,修改本参数后需要重启备库:
recovery_target_timeline = 'latest'
standby_mode = on
primary_conninfo = 'host=10.10.10.1 port=5432 user= u_standby password=standby123 '
trigger_file = '/home/postgres/pg11/trigger'
(2)停掉原主库,会发现原备库变为可读写。
$ pg_ctl stop -m fast
(3)在备库创建trigger_file
$ touch /home/postgres/pg11/trigger
(4)发现原备库变为主库,方法参考“识别当前库主、备角色”。
3、故障的原主库,重新作为备库使用
在异步流复制(async)模式下,主库故障切换后,可能存在原主库故障时还有数据没来及的复制到备库,这些数据将丢失。(注:PostgreSQL的Streaming Replication是以事务为单位,即使数据未同步完毕,也不会出现备库某个事务只恢复一半的情况,因此事务一致性还是可以保证的。)
此种情况下,原主库的最后一个事务时间戳比复制到原备库(新主库)的事务时间戳更新。比如:倒换前最后几个事务是100/101/102,故障前流复制到100事务,则故障切换后,原主库中最新一个事务是102,原备库(新主库)中复制的最后一个事务是100,后续新主库(原备库)将在100的基础上,进行新的事务操作。原主库数据、新主库数据出现分叉点。因此,如果希望原主库恢复服务后作为新备库运行,则需要:
方式一:删库,重搭新备库(详细参考前文备库配置过程)
1、 关闭库,并清空数据(清楚数据即可,不需要重装数据库)
pg_ctl stop -m fast
rm -rf /var/lib/pgsql/data/*
2、 新备库进行数据基本备份
pg_basebackup ….
3、 启动新备库
pg_ctl start
方式二:采用pg_rewind降级为备库,继续服务
如果你的数据库到达TB级别,采用方式一的全量数据基础备份将花费数个小时。为了解决此问题,PostgreSQL9.5引入了pg_rewind功能。原主库(新备库)可以通过pg_rewind操作实现故障时间线的回退。回退后再从新主库中获取最新的后续数据。此时,原主库的数据无须进行重新全量初始化就可以继续进行Streaming Replication,并作为新的Slave使用。
六、监控指标
1. 主库监控
在主库执行下列 sql,可以获得从库的相关信息。不过有些信息都是由从库汇报上来的,比如flush_lsn,replay_lsn,会有一些延迟。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
postgres=# select * from pg_stat_replication; -[ RECORD 1 ]----+------------------------------ pid | 22219 usesysid | 25411 usename | repl application_name | walreceiver client_addr | 192.168.1.2 client_hostname | client_port | 35442 backend_start | 2020-05-06 14:40:58.271771+08 backend_xmin | state | streaming sent_lsn | 0/70209B0 write_lsn | 0/70209B0 flush_lsn | 0/70209B0 replay_lsn | 0/70209B0 write_lag | flush_lag | replay_lag | sync_priority | 0 sync_state | async reply_time | 2020-05-06 14:41:08.308271+08 |
2.从库监控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
postgres=# select * from pg_stat_wal_receiver; -[ RECORD 1 ]---------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- pid | 852 status | streaming receive_start_lsn | 0/7000000 receive_start_tli | 1 received_lsn | 0/7000000 received_tli | 1 last_msg_send_time | 2020-05-06 14:53:59.640178+08 last_msg_receipt_time | 2020-05-06 14:53:59.640012+08 latest_end_lsn | 0/70209B0 latest_end_time | 2020-05-06 14:40:58.293124+08 slot_name | sender_host | 192.168.1.1 sender_port | 15432 conninfo | ... |
关于上面主控监控中,从库的关于 wal 的恢复信息获取会存在延迟。不过我们可以直接在从库上实时获取,
1 2 3 4 5 |
postgres=# select pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp(); pg_last_wal_receive_lsn | pg_last_wal_replay_lsn | pg_last_xact_replay_timestamp -------------------------+------------------------+------------------------------- 0/70209B0 | 0/70209B0 | 2020-04-30 17:15:24.425998+08 (1 row) |
七、参考资料
2. https://www.cnblogs.com/wy20201111/p/14002403.html