合 《PostgreSQL技术内幕——原理探索》第十一章 流复制
Tags: PGPostgreSQL翻译《PostgreSQL技术内幕——原理探索》
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进程可以重放这些数据。 其中walsender和walreceiver 之间使用单条TCP连接进行通信。
在本节中,我们将探讨流复制的启动顺序,以了解这些进程如何启动并且它们之间是如何建立连接的。图11.1显示了流复制的启动顺序图:
图11.1 流复制的启动顺序
- 启动主库服务器和备库服务器。
- 备库服务器启动一个startup进程。
- 备库服务器启动一个walreceiver进程。
- walreceiver向主库服务器发送连接请求。如果主库尚未启动,walreceiver会定期重发该请求。
- 当主库服务器收到连接请求时,将启动walsender进程,并建立walsender和walreceiver之间的TCP连接。
- walreceiver发送备库数据库集簇上最新的LSN。在IT领域中通常将该阶段称作握手(handshaking)。
- 如果备库最新的LSN小于主库最新的LSN(备库的LSN < 主库的LSN),则walsender会将前一个LSN到后一个LSN之间的WAL数据发送到walreceiver。这些WAL数据由存储在主库
pg_xlog
子目录(版本号为10+的更名为pg_wal
)中的WAL段提供。最终,备库重放接收到的WAL数据。在这一阶段,备库在追赶主库,因此被称为追赶(catch-up)阶段。 - 最终,流复制开始工作。
每个walsender进程都维护了连接上的walreceiver或其他应用程序的复制进度状态(请注意,不是连接到walsender的walreceiver或应用程序的本身的状态)。如下是其可能的状态:
- 启动(start-up) —— 从启动walsender到握手结束。如图11.1(5)-(6)。
- 追赶(catch-up) —— 处于追赶期间,如图11.1(7)。
- 流复制(streaming)—— 正在运行流复制。如图11.1(8)。
- 备份(backup)—— 处于向
pg_basebackup
等备份工具发送整个数据库集簇文件的过程中。
系统视图pg_stat_replication
显示了所有正在运行的walsenders的状态,如下例所示:
1 2 3 4 5 6 7 | testdb=# SELECT application_name,state FROM pg_stat_replication; application_name | state ------------------+----------- standby1 | streaming standby2 | streaming pg_basebackup | backup (3 rows) |
如上结果所示,有两个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'
。主库的主要参数如下所示:
1 2 3 | synchronous_standby_names = 'standby1' hot_standby = off wal_level = archive |
另外,在9.5节中提到,有三个情况触发写WAL数据,这里我们只关注事务提交。