PostgreSQL 9.1之前,主从复制传输以WAL日志文件为单位,主库写完一个WAL日志文件后才传送到备库,这种方式导致主备延迟特别大。
9.1引入了主备流复制,传输单位是WAL日志的record,备库不断从主库同步相应的数据,并apply每个WAL record,因此9.1能够做到同步复制。同时9.1提供了Hot Standby,备库在应用WAL record的同时能够提供只读服务,大大提升了用户体验。
PG主备流复制的核心由三个进程组成:
本节探讨流复制的启动顺序,以了解三个核心进程如何启动,以及它们之间如何建立连接
通过pg_stat_replication视图可以查看所有正在运行的walsender状态
SELECT application_name,state FROM pg_stat_replication;
application_name | state
------------------+-----------
standby1 | streaming
standby2 | streaming
pg_basebackup | backup
walsender进程可能的状态如下:
9.4以前,如果备节点请求的wal段在主节点已被覆盖,那么备节点将无法追上主节点。这个问题没有什么好的解决方案,只能把wal_keep_segments参数增大,减少发生的可能性。
9.4开始,这个问题可以使用复制槽(replication slot)来预防——通过暂停walreceiver进程,将含有未发送wal段的pg_xlog保存在复制槽中。复制槽可提高wal数据发送灵活性性,主要用于逻辑复制。
流复制包含两个方面:日志传输和数据同步。
为了更容易理解,本节描述了一主一备的情况,下一节介绍一主多备的情况
假设在备节点处于同步模式、hot_standby参数已禁用、wal_level为'archive',即主库配置以下参数:
synchronous_standby_names = 'standby1'
hot_standby = off
wal_level = archive
假设后台进程在自动提交模式下在主节点发出一条insert语句。后台进程启动事务、发出一条insert语句、然后立即提交事务。我们来探讨如何完成此次提交。
如果wal_level设置为hot_standby或logical,pg会根据commit或abort操作的记录,写入热备功能相关的wal记录
ACK响应将备节点内部信息发送主节点,包含以下4个项目:
walreceiver不仅在写入和刷新WAL数据的时候返回ACK响应,还定期发送备节点心跳(心跳发送间隔通过wal_receiver_status_interval设置,默认10秒)。因此,主节点始终掌握所有已连接备节点的状态。
通过以下查询可以看到所连接备库相关LSN的信息
SELECT application_name AS host,write_location AS write_LSN,flush_location AS flush_LSN,replay_location AS replay_LSN FROM pg_stat_replication;
host | write_lsn | flush_lsn | replay_lsn
----------+-----------+-----------+------------
standby1 | 0/5000280 | 0/5000280 | 0/5000280
standby2 | 0/5000280 | 0/5000280 | 0/5000280
本节描述同步模式下备节点故障时主节点的行为,以及该如何处理这种情况。
即使同步备节点故障,不能再返回ACK响应给主节点,主节点也会继续等待备库的ACK响应。因此,在主节点运行的事务会无法提交,后续的查询也无法执行。换句话说,主节点所有的操作都停止(流复制不支持由于超时自动降级为异步模式)。
有两种方法避免这种情况的发生:
# 1.设置synchronous_standby_names为空串
synchronous_standby_names = ''
#2.执行reload命令重载配置文件
pg_ctl -D $PGDATA reload
以上操作对已经连接的客户端没有影响,主节点会继续处理所有连接的session的事物。
主节点会为自己的所有备节点指定sync_priority(同步优先级)和sync_state(同步状态)。
同步优先级
sync_priority表示备节点在同步模式下的优先级。它是一个固定值,值越小优先级越高,0是个特殊值,表示异步模式。备节点优先级是个有序列表,按synchronous_standby_names中的顺序依次给出。
例如,以下配置中standy1和standy2的优先级分别是1和2。参数中未列出的备节点是异步模式,优先级为0。
synchronous_standby_names = 'standby1,standby2'
同步状态
sync_state表示备节点的状态,这个值由各备节点的运行状态及优先级而定,以下是可能的值:
可以通过pg_stat_replication视图查看这两个值
SELECT application_name AS host,sync_priority,sync_state FROM pg_stat_replication;
host | sync_priority | sync_state
----------+---------------+------------
standby1 | 1 | sync
standby2 | 2 | potential
主节点仅等待Sync状态备节点的ACK响应,换句话说,主节点仅确保Sync状态备节点已写入并刷新wal数据。因此,在流复制中,只有Sync状态备节点与主节点是始终的同步的。
下图展示了Potential状态备库ACK响应早于Sync状态备库的情况:此时主库并不会完成当前事务的提交操作,而要继续等待Sync状态备库ACK响应。当收到Sync状态备库ACK响应时,主库后端进程才释放latch并完成事务提交。
这里standby1和standby2的sync_state分别是sync和potentail。
相反,如果主库先接收到Sync状态备库ACK响应,它会立即完成当前事物提交,而不去确认potentail状态的备库是否已写入并刷新wal数据。
Potential或Async状态备节点故障
主节点会终止连接到故障备节点的walsender进程,并继续进行自己的事务处理。换句话说,主节点的事务处理不受这两类备节点故障的影响。
sync状态备节点故障
主节点终止连接到故障节点的walsender进程,Potential状态备节点顶替故障的备节点变为Sync状态(期间主库不可用),在替换完成后主库恢复事务处理。因此,备节点的故障检测(下节介绍)对提高流复制高可用至关重要。
流复制使用两种常见的故障检测程序,不需要任何特殊的硬件。
如果walreceiver在wal_sender_timeout内(默认60s)没有返回任何结果,主节点会认为备节点出现故障。与上述故障对比,即使备节点由于硬件、网络等故障已无法返回任何响应,主库也需要最长wal_sender_timeout的时间来确认备节点故障。
根据故障类型,在故障和检测之间可能会有时间差,特别是如果在Sync状态备库中发生第2种故障,那么即使有多个Potential状态备节点正常工作,检测到Sync状态备库失效,主库仍然可能会有一段时间不可用。
在9.2及之前版本,wal_sender_timeout参数被称为replication_timeout。
参考
《postgresql指南 内幕探索》
http://mysql.taobao.org/monthly/2015/10/04/