谈谈PostgreSQL的数据复制
前言
复制是任何数据库系统中至关重要的一部分,旨在提供高可用性(HA)和有效的灾难恢复(DR)策略。本文旨在阐明复制在数据库系统中的作用。文章将对复制及其类型进行总体概述,并介绍 PostgreSQL 中的复制选项。
“复制”一词用来描述在一个或多个软件或硬件系统之间共享信息的过程,以确保系统的可靠性、可用性和容错性。这些系统可以位于同一地点,可以在单台机器上,也可以通过广域网络相连。复制大致可以分为硬件和软件两大类。我们将简单探讨这两类,但本文的主要关注点是数据库复制。那么,首先让我们了解构成数据库复制系统的要素。
简而言之,数据库复制描述的是将数据从一个数据库实例复制到一个或多个数据库实例的过程。同样,这些实例可以位于同一位置,也可以通过广域网络相连。
基于硬件的复制
我们先从硬件复制开始。基于硬件的复制使多个连接的系统保持同步。这种数据同步在存储层面完成,一旦系统执行了 I/O 操作,就会将操作传播到配置好的存储模块、设备或系统中。这种复制既可以针对整个存储设备进行,也可以仅针对选定的分区。此类解决方案的最大优势在于(通常)更易于部署,并且与软件无关,这使得其性能更优,但同时也降低了对复制过程的灵活性和控制。以下是该复制类型的一些优点:
- 实时性 —— 所有更改都会立即应用到后续系统中。
- 易于设置 —— 无需编写脚本或进行软件配置。
- 独立于应用 —— 复制发生在存储层,与操作系统或应用程序无关。
- 数据完整性和一致性 —— 由于镜像操作在存储层进行,从而能够精确复制存储磁盘,因此能自动确保数据的完整性和一致性。
尽管基于硬件的复制具有一些非常吸引人的优点,但它也存在自身的局限性。通常这类复制依赖于供应商锁定,即必须使用相同类型的硬件,而且往往成本较高。
基于软件的复制
这种复制方式既包括通用解决方案,也包括针对特定产品的解决方案。通用解决方案通常在软件层面上模仿硬件复制,通过在不同系统间复制数据来实现复制。负责执行复制的软件会将写入源存储的每一位数据复制并传送到目标系统。而针对特定产品的解决方案则更侧重于满足产品的特定需求,通常专为某一产品量身定制。
基于软件的复制各有优缺点。一方面,它提供了复制过程的灵活性和控制能力,通常成本较低,同时具备更丰富的功能;但另一方面,它需要大量配置,并且需要持续的监控和维护。
数据库复制
在讨论完复制及其不同类型后,让我们把注意力转向数据库复制这一主题。
在进一步详细说明之前,先来讨论一下在数据库领域中用于描述复制系统各组件的不同术语。常用的术语包括 Primary-Standby、Master-Slave、Publisher-Subscriber 以及 Master-Master/Multimaster,它们都用于描述参与复制设置的数据库服务器。
术语 “Primary”、“Master” 和 “Publisher” 用来描述那些主动节点,这些节点负责将接收到的更改传播给其他节点;而 “Standby”、“Slave” 和 “Subscriber” 则用于描述那些被动节点,这些节点负责接收来自主动节点传播的更改。本文中,我们将使用 “Primary” 表示主动节点,使用 “Standby” 表示被动节点。
数据库复制可以配置为 Primary-Standby 配置和 Multi-master 配置。
在 Primary-Standby 配置中,只有一个实例接收数据更改,然后将这些更改分发给各个 Standby 节点。
而在多主系统中,每个数据库实例都可以接收数据更改,并将这些更改传播给其他实例。
同步/异步复制
复制过程的核心在于 Primary 节点将数据传输给 Standby 节点的能力。与其他数据传输策略类似,这一过程可以以同步方式进行,即 Primary 节点在等待所有 Standby 节点确认已接收并写入数据到磁盘后,再向客户端发送确认。或者,Primary 节点可以先在本地提交数据,然后在适当时机将事务数据传输给 Standby 节点,并期望 Standby 节点能够接收并写入数据到磁盘。在这种情况下,Standby 节点不会向 Primary 节点发送确认。前一种策略称为同步复制,而后一种则称为异步复制。无论是基于硬件还是基于软件的解决方案,都支持同步和异步复制。
由于这两种策略的主要区别在于是否等待被动节点确认数据写入,因此各有优缺点。同步复制可以配置为所有系统都保持最新状态,实现实时复制;但与此同时,由于主节点需要等待被动节点的确认,这会对系统性能产生不利影响。
相比之下,异步复制通常能提供更好的性能,因为它不需要等待来自被动节点的确认消息。
级联复制
到目前为止,我们看到只有 Primary 节点将事务数据传输给 Standby 节点。但这个负载可以分摊到多个系统上;也就是说,一个已接收数据并写入磁盘的 Standby 节点可以将数据继续传输给配置为从它接收数据的其他 Standby 节点。这种方式称为级联复制,即在 Primary 与 Standby 之间,以及 Standby 与 Standby 之间配置复制。下图直观地展示了级联复制的过程。
备用模式
- Warm standby 用于描述一种备用服务器,这种服务器允许接收来自主节点的更改,但不允许客户端直接连接。
- Hot standby 用于描述一种备用服务器,它不仅允许接收来自主节点的更改,还允许接受客户端连接。
PostgreSQL 复制
最后,我想介绍一下 PostgreSQL 提供的复制选项。
PostgreSQL 提供内置的日志流复制和逻辑复制选项。其中,内置复制仅适用于主备(Primary-Standby)配置。
流复制(SR)
在 PostgreSQL 中,流复制也被称为物理复制或二进制复制。二进制数据被传输至备用服务器。为了实现这一点,流复制使用了 WAL(预写日志)记录。任何在主服务器上进行的更改都会先写入 WAL 文件,然后再写入磁盘,数据库服务器具备将这些日志记录传输到任意数量备用服务器的能力。由于这种复制方式是数据的二进制拷贝,因此也存在一些限制。此类型的复制仅适用于需要在备用服务器上复制所有更改的情况,如果只需要复制部分更改,则无法使用。此外,备用服务器要么不接受连接,要么只能处理只读查询。另一个限制是,该复制方式不能与不同版本的数据库服务器混用,整个系统中所有数据库服务器的版本必须一致。
默认情况下,流复制是异步的,不过开启同步复制也并不困难。这种配置非常适合实现高可用性(HA)。如果主服务器发生故障,由于备用服务器几乎是其完全相同的拷贝,其中一台即可接替主服务器的工作。以下是基本配置示例:
- 首先,在主数据库中创建一个具有复制权限的用户,该用户将用于备用服务器与主服务器建立连接:
1 | CREATE USER foouser REPLICATION; |
- 将该用户添加到 pg_hba.conf 文件中,以允许其进行身份验证:
1 | # TYPE DATABASE USER ADDRESS METHOD |
- 在备用服务器上执行基准备份:
1 | pg_basebackup -h <primary-ip> -U foouser -D ~/standby |
- 在 postgresql.conf 文件中,需要添加以下配置项:
1 | wal_level=replica # 对于流复制,应将其设置为 replica |
- 在备用服务器上,在其数据目录下创建 recovery.conf 文件,并添加以下内容:
1 | standby_mode=on |
现在启动服务器,基本的流复制即可生效。
逻辑复制
与流复制相比,逻辑复制在 PostgreSQL 中相对较新。流复制是对整个数据的逐字节复制,但它不支持将单个表或部分数据从主服务器复制到备用服务器。逻辑复制则允许只复制数据库中指定的对象到备用服务器,而不是复制整个数据库。它还支持在不同版本的数据库服务器之间进行数据复制,并且允许备用服务器既接受读请求,也接受写请求。
然而,需要注意的是,逻辑复制尚未实现冲突解决机制。因此,如果主服务器和备用服务器都被允许对同一张表进行写操作,很可能会出现数据冲突,从而中断复制过程。在这种情况下,用户必须手动解决冲突。不过,如果主服务器和备用服务器分别只对完全不重叠的数据集进行写操作,就可以避免冲突解决的问题。
以下是基本配置示例:
- 首先,在主数据库中创建一个具有复制权限的用户,该用户将用于备用服务器与主服务器建立连接:
1 | CREATE USER foouser REPLICATION; |
- 将该用户添加到 pg_hba.conf 文件中,以允许其进行身份验证:
1 | # TYPE DATABASE USER ADDRESS METHOD |
- 对于逻辑复制,基准备份不是必需的,任何一个数据库实例都可以用来创建该设置。
1 | wal_level=logical # 对于逻辑复制,wal_level 需要在 postgresql.conf 文件中设置为 logical。 |
- 在主服务器上,创建一些用于复制的表,并创建一个发布,列出这些表。
1 | CREATE TABLE t1 (col1 int, col2 varchar); |
- 在备用服务器上,需要创建上述提到的表的结构,因为 DDL 不会被复制。
1 | CREATE TABLE t1 (col1 int, col2 varchar); |
- 在备用服务器上,为上述发布创建一个订阅。
1 | CREATE SUBSCRIPTION foosub CONNECTION 'host=<primary-ip> port=<primary-port> user=foouser' PUBLICATION foopub; |
现在启动服务器,基本的流复制应即可生效。
希望这篇博客能帮助您了解复制的一般概念,以及从 PostgreSQL 的角度理解流复制和逻辑复制的原理。此外,也希望这能助您更好地设计出满足您需求的复制环境。