《PostgreSQL技术内幕——原理探索》第十一章 流复制

0    196    1

Tags:

👉 本文共约8941个字,系统预计阅读时间或需34分钟。

PostgreSQL在9.1版本中实现了流复制。它属于所谓的一主多从类型的复制,而这两个术语 —— 主(master)从(slave),在PostgreSQL中通常分别被称为主(primary)备(standby)

译注:存储数据库副本的每个节点称为副本(replica)。每一次对数据库的写入操作都需要传播到所有副本上,否则副本就会包含不一样的数据。最常见的解决方案被称为基于领导者的复制(leader-based replication),也称主动/被动(active/passive)主/从(master/slave)复制。其中,副本之一被指定为领导者(leader),也称为 主库(master)首要(primary)。当客户端要向数据库写入时,它必须将请求发送给领导者,领导者会将新数据写入其本地存储。其他副本被称为追随者(followers),亦称为只读副本(read replicas)从库(slaves)次要( sencondaries)热备(hot-standby)

这种原生复制功能是基于日志传输实现的,这是一种通用的复制技术:主库不断发送WAL数据,而每个备库接受WAL数据,并立即重放日志。

本章将介绍以下主题,重点介绍流复制的工作原理:

  • 流复制是如何启动的
  • 数据是如何在主备之间传递的
  • 主库如何管理多个备库
  • 主库如何检测到备库的失效

尽管在9.0版本中最初实现的复制功能只能进行异步复制,它很快就在9.1版中被新的实现(如今采用的)所替代,可以支持同步复制。

11.1 流复制的启动

在流复制中,有三种进程协同工作。首先,主库上的walsender(WAL发送器)进程将WAL数据发送到备库;同时,备库上的walreceiver(WAL接收器)在接收这些数据,而备库上的startup进程可以重放这些数据。 其中walsenderwalreceiver 之间使用单条TCP连接进行通信。

在本节中,我们将探讨流复制的启动顺序,以了解这些进程如何启动并且它们之间是如何建立连接的。图11.1显示了流复制的启动顺序图:

图11.1 流复制的启动顺序

img

  1. 启动主库服务器和备库服务器。
  2. 备库服务器启动一个startup进程。
  3. 备库服务器启动一个walreceiver进程。
  4. walreceiver向主库服务器发送连接请求。如果主库尚未启动,walreceiver会定期重发该请求。
  5. 当主库服务器收到连接请求时,将启动walsender进程,并建立walsenderwalreceiver之间的TCP连接。
  6. walreceiver发送备库数据库集簇上最新的LSN。在IT领域中通常将该阶段称作握手(handshaking)
  7. 如果备库最新的LSN小于主库最新的LSN(备库的LSN < 主库的LSN),则walsender会将前一个LSN到后一个LSN之间的WAL数据发送到walreceiver。这些WAL数据由存储在主库pg_xlog子目录(版本号为10+的更名为pg_wal)中的WAL段提供。最终,备库重放接收到的WAL数据。在这一阶段,备库在追赶主库,因此被称为追赶(catch-up)阶段。
  8. 最终,流复制开始工作。

每个walsender进程都维护了连接上的walreceiver或其他应用程序的复制进度状态(请注意,不是连接到walsenderwalreceiver或应用程序的本身的状态)。如下是其可能的状态:

  • 启动(start-up) —— 从启动walsender到握手结束。如图11.1(5)-(6)。
  • 追赶(catch-up) —— 处于追赶期间,如图11.1(7)。
  • 流复制(streaming)—— 正在运行流复制。如图11.1(8)。
  • 备份(backup)—— 处于向pg_basebackup等备份工具发送整个数据库集簇文件的过程中。

系统视图pg_stat_replication显示了所有正在运行的walsenders的状态,如下例所示:

如上结果所示,有两个walsender正在运行,其正在向连接的备库发送WAL数据,另一个walsender在向pg_basebackup应用发送所有数据库集簇中的文件。

### 在备库长时间停机后,如果重启会发生什么?

在9.3版及更早版本中,如果备库所需的WAL段在主库上已经被回收了,备库就无法追上主库了。这一问题并没有可靠的解决方案,只能为参数wal_keep_segments配置一个较大的值,以减少这种情况发生的可能性,但这只是权宜之计。

在9.4及后续版本中,可以使用复制槽(replications slot)来预防此问题发生。复制槽是一项提高WAL数据发送灵活性的功能。主要是为逻辑复制(logical replication)而提出的,同时也能解决这类问题 ——复制槽通过暂停回收过程,从而保留pg_xlog(10及后续版本的pg_wal)中含有未发送数据的的WAL段文件,详情请参阅官方文档

11.2 如何实施流复制

流复制有两个方面:日志传输和数据库同步。因为流复制基于日志,日志传送显然是其中的一个方面 —— 主库会在写入日志记录时,将WAL数据发送到连接的备库。同步复制中需要数据库同步 —— 主库与多个备库通信,从而同步整个数据库集簇。

为准确理解流复制的工作原理,我们应该探究下主库如何管理多个备库。为了尽可能简化问题,本节描述了一个特例(即单主单备系统),而下一节将描述一般情况(单主多备系统)。

11.2.1 主从间的通信

假设备库处于同步复制模式,但配置参数hot-standby已禁用,且wal_level'archive'。主库的主要参数如下所示:

另外,在9.5节中提到,有三个情况触发写WAL数据,这里我们只关注事务提交。

假设主库上的一个后端进程在自动提交模式下发出一个简单的INSERT语句。后端启动事务,发出INSERT语句,然后立即提交事务。让我们进一步探讨此提交操作如何完成的。如图11.2中的序列图:

图11.2 流复制的通信序列图

img

  1. 后端进程通过执行函数XLogInsert()XLogFlush(),将WAL数据写入并刷新到WAL段文件中。
  2. walsender进程将写入WAL段文件的WAL数据发送到walreceiver进程。
  3. 在发送WAL数据之后,后端进程继续等待来自备库的ACK响应。更确切地说,后端进程通过执行内部函数SyncRepWaitForLSN()来获取锁存器(latch),并等待它被释放。
  4. 备库上的walreceiver通过write()系统调用,将接收到的WAL数据写入备库的WAL段,并向walsender返回ACK响应。
  5. walreceiver通过系统调用(例如fsync())将WAL数据刷新到WAL段中,向walsender返回另一个ACK响应,并通知启动进程(startup process )相关WAL数据的更新。
  6. 启动进程重放已写入WAL段的WAL数据。
  7. walsender在收到来自walreceiver的ACK响应后释放后端进程的锁存器,然后,后端进程完成commitabort动作。 锁存器释放的时间取决于参数synchronous_commit。如果它是'on'(默认),当接收到步骤(5)的ACK时,锁存器被释放。而当它是'remote_write'时,接收到步骤(4)的ACK时,即被释放。

如果配置参数wal_level'hot_standby''logical',则PostgreSQL会根据COMMITABORT操作的记录,写入热备功能相关的WAL记录。(在这个例子中,PostgreSQL不写那些记录,因为它是'archive'。)

每个ACK响应将备库的内部信息通知给主库。包含以下四个项目:

  • 已写入最新WAL数据的LSN位置。
  • 已刷新最新WAL数据的LSN位置。
  • 启动进程已经重放最新的WAL数据的LSN。
  • 发送此响应的时间戳。

walreceiver不仅在写入和刷新WAL数据时返回ACK响应,而且还定期发送备库的心跳响应。因此,主库始终掌握所有连接备库的状态。

执行如下查询,可以显示所连接备库的相关LSN信息。

心跳的间隔设置为参数wal_receiver_status_interval,默认为10秒。

11.2.2 发生故障时的行为

在本小节中,将介绍在同步备库发生故障时,主库的行为方式,以及主库会如何处理该情况。

即使同步备库发生故障,且不再能够返回ACK响应,主库也会继续等待响应。因此,正在运行的事务无法提交,而后续查询也无法启动。换而言之,实际上主库的所有操作都已停止(流复制不支持发生超时时自动降级回滚到异步模式的功能)。

有两种方法可以避免这种情况。其中之一是使用多个备库来提高系统可用性,另一个是通过手动执行以下步骤从同步模式切换到异步模式。

  1. 将参数synchronous_standby_names的值设置为空字符串。

  2. 使用reload选项执行pg_ctl命令。

上述过程不会影响连接的客户端。主库继续事务处理,以及会保持客户端与相应的后端进程之间的所有会话。

11.2 流复制如何实施

流式复制有两个部分:日志传输与数据库同步。日志传输是很明显的部分,因为流复制正是基于此的 —— 每当主库发生写入时,它会向所有连接着的备库发送WAL数据。数据库同步对于同步复制而言则是必需的 —— 主库与多个备库中的每一个相互沟通,以便各自的数据库集簇保持同步。

为了准确理解流复制的工作原理,我们应当研究主库是如何管理多个备库的。为了简单起见,下面的小节将会描述一种特殊场景(即一主一从的情况),而通用的场景(一主多从)会在更后面一个小节中描述。

11.2.1 主库与同步备库之间的通信

假设备库处于同步复制模式,但参数hot_standby被配置为禁用,而wal_level被配置为archive,而主库上的主要参数如下所示:

除了在9.5节中提到过的三种操作外,我们在这里主要关注事务的提交。

假设主库上一个后端进程在自动提交模式中发起了一条INSERT语句。首先,后端进程开启了一个事务,执行INSERT语句,然后立即提交。让我们深入研究一下这个提交动作是如何完成的,如下面的序列图11.2。

本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信dbaup66,谢谢!
AiDBA后续精彩内容已被站长无情隐藏,请输入验证码解锁本文!
验证码:
获取验证码: 请先关注本站微信公众号,然后回复“验证码”,获取验证码。在微信里搜索“AiDBA”或者“dbaup6”或者微信扫描右侧二维码都可以关注本站微信公众号。

标签:

Avatar photo

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复