pg主从复制(一)——流复制机制

PostgreSQL 9.1之前,主从复制传输以WAL日志文件为单位,主库写完一个WAL日志文件后才传送到备库,这种方式导致主备延迟特别大。

9.1引入了主备流复制,传输单位是WAL日志的record,备库不断从主库同步相应的数据,并apply每个WAL record,因此9.1能够做到同步复制。同时9.1提供了Hot Standby,备库在应用WAL record的同时能够提供只读服务,大大提升了用户体验。

 

一、 主备流复制架构

PG主备流复制的核心由三个进程组成:

  • walsender:用于主库发送WAL日志记录至从库
  • walreceiver:用于从库接收主库的WAL日志记录
  • startup:用于从库apply日志

pg主从复制(一)——流复制机制_第1张图片

 

二、 流复制的启动

1. 启动过程

本节探讨流复制的启动顺序,以了解三个核心进程如何启动,以及它们之间如何建立连接

pg主从复制(一)——流复制机制_第2张图片

  • (1)启动主、备服务器
  • (2)备节点启动startup进程
  • (3)备节点启动walreceiver进程
  • (4)walreceiver进程向主节点发送连接请求,如果主库尚未启动,walreceiver会定期重发该请求
  • (5)当主节点收到连接请求时,将启动walsender进程,并建立walsender与walreceiver之间的TCP连接
  • (6)walreceiver发送备节点最新的LSN,这个阶段在IT领域称为握手机制
  • (7)如果备库最新LSN小于主库最新LSN(落后),walsender会将前一个LSN到后一个LSN之间的wal数据发送到walreceiver。这个阶段就是备库追赶主库的阶段。
  • (8)流复制开始工作

 

2. walsender状态

通过pg_stat_replication视图可以查看所有正在运行的walsender状态

SELECT application_name,state FROM pg_stat_replication;

 application_name |   state
------------------+-----------
 standby1         | streaming
 standby2         | streaming
 pg_basebackup    | backup

walsender进程可能的状态如下:

  • start-up:上图(5)~(6)
  • catch-up:上图(7)
  • streaming:上图(8)
  • backup:由于备份发送整个数据库集群的文件,比如pg_basebackup

 

3. 备节点长期停机再启动后,会发生什么?

9.4以前,如果备节点请求的wal段在主节点已被覆盖,那么备节点将无法追上主节点。这个问题没有什么好的解决方案,只能把wal_keep_segments参数增大,减少发生的可能性。

9.4开始,这个问题可以使用复制槽(replication slot)来预防——通过暂停walreceiver进程,将含有未发送wal段的pg_xlog保存在复制槽中。复制槽可提高wal数据发送灵活性性,主要用于逻辑复制。

 

三、 流复制过程

流复制包含两个方面:日志传输和数据同步。

  • 流复制是基于日志传输的,主节点会在写入日志记录时,将WAL数据发送到已连接的备节点。
  • 同步复制需要数据库同步,主节点与多个备节点通信,从而同步整个数据库集群。

 

为了更容易理解,本节描述了一主一备的情况,下一节介绍一主多备的情况

1. 主备间的通信

假设在备节点处于同步模式、hot_standby参数已禁用、wal_level为'archive',即主库配置以下参数:

synchronous_standby_names = 'standby1'
hot_standby = off
wal_level = archive

假设后台进程在自动提交模式下在主节点发出一条insert语句。后台进程启动事务、发出一条insert语句、然后立即提交事务。我们来探讨如何完成此次提交。

pg主从复制(一)——流复制机制_第3张图片

  • (1)后台进程执行XLogInsert()和 XLogFlush()函数,将wal数据写入缓存并flush到wal段文件。
  • (2)walsender将写入wal段文件的wal数据发送到walreceiver。
  • (3)主节点发送wal数据后,后端进程继续等待来自备节点的ACK响应。更确切地说,后台进程执行内部函数SyncRepWaitForLSN()来获取latch,并等待释放它。
  • (4)walreceiver通过write()函数将接收到的wal数据写入备节点的wal缓存,并且返回ACK响应给walsender。
  • (5)walreceiver通过fsync()函数将wal数据全部flush到wal段文件,并且再返回一个ACK响应给walsengder,通知startup进程wal相关数据已更新。
  • (6)starup进程应用已经写入wal段文件的wal数据。
  • (7)walsender在接受到ACK响应后释放latch,然后后端进程完成commit或abort动作。latch释放的时间取决于参数synchronous_commit(详细参考下片):如果参数是on,则在步骤(5)接受ACK响应后释放latch;如果是remote_write,那么在步骤(4)接受ACK响应后就释放latch。

如果wal_level设置为hot_standby或logical,pg会根据commit或abort操作的记录,写入热备功能相关的wal记录

 

2. ACK响应内容

ACK响应将备节点内部信息发送主节点,包含以下4个项目:

  • 已写入的最新WAL数据的LSN位置
  • 已刷新的最新WAL数据的LSN位置
  • startup进程最新应用的wal数据的LSN位置
  • 发送此ACK的时间戳

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

 

3. 发生故障时的行为

本节描述同步模式下备节点故障时主节点的行为,以及该如何处理这种情况。

即使同步备节点故障,不能再返回ACK响应给主节点,主节点也会继续等待备库的ACK响应。因此,在主节点运行的事务会无法提交,后续的查询也无法执行。换句话说,主节点所有的操作都停止(流复制不支持由于超时自动降级为异步模式)。

有两种方法避免这种情况的发生:

  • 提供多台备节点来提高系统的可用性
  • 通过手动执行以下步骤从同步流复制转换到异步流复制
# 1.设置synchronous_standby_names为空串
synchronous_standby_names = ''

#2.执行reload命令重载配置文件
pg_ctl -D $PGDATA reload

以上操作对已经连接的客户端没有影响,主节点会继续处理所有连接的session的事物。

 

四、 管理多个备节点

 

1. 同步优先级与同步状态

主节点会为自己的所有备节点指定sync_priority(同步优先级)和sync_state(同步状态)。

同步优先级

sync_priority表示备节点在同步模式下的优先级。它是一个固定值,值越小优先级越高,0是个特殊值,表示异步模式。备节点优先级是个有序列表,按synchronous_standby_names中的顺序依次给出。

例如,以下配置中standy1和standy2的优先级分别是1和2。参数中未列出的备节点是异步模式,优先级为0。

synchronous_standby_names = 'standby1,standby2'

同步状态

sync_state表示备节点的状态,这个值由各备节点的运行状态及优先级而定,以下是可能的值:

  • Sync:具有最高优先级的同步模式备节点状态
  • Potential:同步模式下优先级>=2的备节点状态。如果Sync状态(最高优先级)的备节点故障,优先级第二高(优先级=2)的备节点会代替故障节点变为最高优先级。
  • ASync:异步模式备节点的固定值,除非修改同步模式,否则它们的状态永远不会是sync和potential。

可以通过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

 

2. 主节点如何管理多个备节点

主节点仅等待Sync状态备节点的ACK响应,换句话说,主节点仅确保Sync状态备节点已写入并刷新wal数据。因此,在流复制中,只有Sync状态备节点与主节点是始终的同步的。

下图展示了Potential状态备库ACK响应早于Sync状态备库的情况:此时主库并不会完成当前事务的提交操作,而要继续等待Sync状态备库ACK响应。当收到Sync状态备库ACK响应时,主库后端进程才释放latch并完成事务提交。

pg主从复制(一)——流复制机制_第4张图片

这里standby1和standby2的sync_state分别是sync和potentail。

  • 尽管主库已接收到Potential状态备库ACK响应,但主库的后端进程还是持续等待Sync状态备节点ACK响应
  • 接收到Sync状态备节点ACK响应后,主库后端进程释放latch,完成当前的事物提交

相反,如果主库先接收到Sync状态备库ACK响应,它会立即完成当前事物提交,而不去确认potentail状态的备库是否已写入并刷新wal数据。

 

3. 备库发生故障时的行为

Potential或Async状态备节点故障

主节点会终止连接到故障备节点的walsender进程,并继续进行自己的事务处理。换句话说,主节点的事务处理不受这两类备节点故障的影响。

 

sync状态备节点故障

主节点终止连接到故障节点的walsender进程,Potential状态备节点顶替故障的备节点变为Sync状态(期间主库不可用),在替换完成后主库恢复事务处理。因此,备节点的故障检测(下节介绍)对提高流复制高可用至关重要。

pg主从复制(一)——流复制机制_第5张图片

 

五、 备节点的故障检测

流复制使用两种常见的故障检测程序,不需要任何特殊的硬件。

 

1. 备节点进程的故障检测

  • 当检测到walsender与walreceiver的连接中断时,主节点立即判定备节点或walreceiver出现故障。
  • 当底层网络函数由于未能成功读/写walreceiver套接字接口而返回错误时,主节点也会立即判定其失效。

 

2.硬件或者网络故障检测

如果walreceiver在wal_sender_timeout内(默认60s)没有返回任何结果,主节点会认为备节点出现故障。与上述故障对比,即使备节点由于硬件、网络等故障已无法返回任何响应,主库也需要最长wal_sender_timeout的时间来确认备节点故障。

 

根据故障类型,在故障和检测之间可能会有时间差,特别是如果在Sync状态备库中发生第2种故障,那么即使有多个Potential状态备节点正常工作,检测到Sync状态备库失效,主库仍然可能会有一段时间不可用。

在9.2及之前版本,wal_sender_timeout参数被称为replication_timeout。

 

六、 进程通信详细过程

 

1. walsender和walreceiver的流复制过程

pg主从复制(一)——流复制机制_第6张图片

2. walreceiver和startup进程

pg主从复制(一)——流复制机制_第7张图片

参考

《postgresql指南 内幕探索》

http://mysql.taobao.org/monthly/2015/10/04/

你可能感兴趣的:(PostgreSQL,灾备,架构)