受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

Chapter 26. 高可用、负载均衡和复制

数据库服务器可以协同工作,从而在主库失效时让第二台服务器快速接管其任务(高可用性),或者让多台计算机提供同一份数据(负载均衡)。理想情况下,数据库服务器应当能够无缝协同工作。提供静态网页服务的 Web 服务器只需把 Web 请求分发到多台机器,就能很容易地组合起来。事实上,只读数据库服务器也相对容易组合起来。不幸的是,大多数数据库服务器同时处理读写请求,而读/写服务器要更难组合。这是因为,只读数据只需要在每台服务器上放置一次,而对任意一台服务器的写入都必须传播到所有服务器,以保证后续对这些服务器的读取请求返回一致的结果。

这种同步问题是服务器协同工作的根本难点。由于不存在一种能够对所有使用场景都消除同步问题影响的单一方案,因此出现了多种不同的解决方案。每一种方案都以不同方式处理这个问题,并且针对特定负载将其影响降到最低。

某些方案通过只允许一台服务器修改数据来处理同步。能够修改数据的服务器称为读/写、主库服务器。跟踪主库变更的服务器称为备库次级服务器。只能在被提升为主库之后才能连接的备库称为温备,而能够接受连接并提供只读查询的备库称为热备

某些方案是同步的,即一个修改数据的事务只有在所有服务器都提交该事务之后才被视为已提交。这保证一次故障转移不会丢失任何数据,并且所有负载均衡的服务器无论查询哪一台都将返回一致的结果。相反,异步方案允许一次提交与其传播到其他服务器之间存在一定延迟,这就带来了切换到备库时丢失某些事务的可能性,也意味着负载均衡的服务器可能返回略微陈旧的结果。当同步通信过慢时,就会使用异步通信。

这些方案还可以按粒度分类。有些方案只能处理整个数据库服务器,而另一些则允许在每个表或者每个数据库级别进行控制。

无论做出哪种选择,都必须考虑性能。功能与性能之间通常存在权衡。例如,在低速网络上采用完全同步的方案,性能可能下降一半以上,而异步方案对性能的影响则可能很小。

本节其余部分将概述多种故障转移、复制和负载均衡方案。

26.1. 不同方案的比较 #

共享磁盘故障转移

共享磁盘故障转移通过只保留一份数据库拷贝来避免同步开销。它使用一个由多个服务器共享的单一磁盘阵列。如果主库失效,备库就可以挂载并启动数据库,就好像它正在从一次数据库崩溃中恢复一样。这允许快速故障转移而不会丢失数据。

共享硬件功能常见于网络存储设备。使用网络文件系统也是可行的,但必须注意文件系统是否具备完整的 POSIX 行为(见Section 18.2.2.1)。这种方法的一大局限是,如果共享磁盘阵列失效或损坏,主库和备库都会无法工作。另一个问题是,在主库运行期间,备库绝不应访问共享存储。

文件系统(块设备)复制

共享硬件功能的一种变体是文件系统复制,其中对一个文件系统的所有更改都会镜像到位于另一台计算机上的文件系统。唯一的限制是,这种镜像必须以能够保证备库拥有该文件系统一致拷贝的方式完成 — 特别是,对备库的写入必须与主库上的写入保持相同顺序。DRBD 是 Linux 上一种流行的文件系统复制方案。

预写式日志传送

温备和热备服务器能够通过读取预写式日志(WAL)记录流来保持最新状态。如果主库失效,备库拥有主库的几乎全部数据,并且能够迅速成为新的主库。这可以是同步的,也可以是异步的,并且只能用于整个数据库服务器。

可以使用基于文件的日志传送(Section 26.2)、流复制(见 Section 26.2.5)或两者结合的方式来实现备库。有关热备的信息,见 Section 26.4

逻辑复制

逻辑复制允许数据库服务器将数据修改流发送到另一台服务器。PostgreSQL 逻辑复制从 WAL 构造逻辑数据修改流。逻辑复制允许按表粒度复制数据变更。此外,发布自身数据更新的服务器也可以订阅其他服务器的更改,从而允许数据在多个方向上流动。有关逻辑复制的更多信息,请参考Chapter 29。通过逻辑解码接口(Chapter 47),第三方扩展也能提供类似的功能。

基于触发器的主库-备库复制

基于触发器的复制通常会将修改数据的查询汇聚到指定的主库。它按表粒度工作,主库(通常)会把数据更改异步发送到备库。主库运行期间,备库可以响应查询,并且可能允许某些本地数据更改或写入活动。这种复制形式常用于卸载大型分析型查询或数据仓库查询。

Slony-I 是这种复制类型的一个示例。它按表粒度工作,并且支持多个备库。由于它会以批处理方式异步更新备库,因此在故障转移期间可能会发生数据丢失。

基于 SQL 的复制中间件

在基于 SQL 的复制中间件中,一个程序会拦截每一条 SQL 查询,并将其发送到一台或所有服务器。每台服务器独立运行。读写查询必须发送到所有服务器,这样每台服务器都会收到所有变更;但只读查询可以只发送到某一台服务器,从而把读取负载分散到多台服务器之间。

如果只是原样广播查询,那么诸如random()CURRENT_TIMESTAMP以及序列之类的对象,在不同服务器上可能得到不同的值。这是因为每台服务器都是独立运行的,而且被广播的是 SQL 查询,而不是真正的数据变更。如果这不可接受,那么中间件或应用程序就必须从单一来源确定这些值,然后在写查询中使用这些值。还必须注意保证所有事务在所有服务器上要么都提交,要么都中止,这可能需要使用两阶段提交(PREPARE TRANSACTIONCOMMIT PREPARED)。Pgpool-IIContinuent Tungsten就是这类复制的示例。

异步多主复制

对于那些并不经常连接或通信链路较慢的服务器,例如笔记本电脑或远程服务器,保持服务器间的数据一致是一个挑战。使用异步多主复制时,每台服务器都独立工作,并定期与其他服务器通信,以识别冲突事务。这些冲突可以由用户或冲突解决规则来解决。Bucardo 是这种复制类型的一个示例。

同步多主复制

在同步多主复制中,每台服务器都能接受写请求,并且在每个事务提交之前,修改过的数据都会从原始服务器传送给其他每台服务器。繁重的写入活动可能导致过多锁定和提交延迟,进而带来较差的性能。读请求可以发送给任意服务器。某些实现使用共享磁盘来减少通信开销。同步多主复制尤其适合以读为主的负载,尽管它的一大优势是任意服务器都能接受写请求 — 无需在主库和备库之间划分负载,并且由于数据更改是从一台服务器传送到另一台服务器,因此不会出现非确定函数(如 random())的问题。

PostgreSQL 本身不提供这种复制,不过可以在应用代码或中间件中利用 PostgreSQL 的两阶段提交(PREPARE TRANSACTIONCOMMIT PREPARED)来实现这一功能。

Table 26.1总结了上述多种方案的能力。

Table 26.1. 高可用、负载均衡和复制特性矩阵

特性 共享磁盘 文件系统复制 预写式日志传送 逻辑复制 基于触发器的复制 基于 SQL 的复制中间件 异步多主复制 同步多主复制
常见示例 NAS DRBD 内置流复制 内置逻辑复制、pglogical Londiste,Slony pgpool-II Bucardo  
通信方法 共享磁盘 磁盘块 WAL 逻辑解码 表行 SQL 表行 表行和行锁
不要求特殊硬件  
允许多个主库        
主库无额外负载        
不等待多个服务器   关闭同步时 关闭同步时    
主库失效时绝不丢失数据 启用同步时 启用同步时    
副本接受只读查询     启用热备时
每个表粒度        
不需要冲突解决    

有一些方案不适合上述的类别:

数据分区

数据分区会把表拆分成多个数据集。每个数据集只能由一台服务器修改。例如,可以按办公室划分数据,如伦敦和巴黎,每个办公室各有一台服务器。如果有必要执行组合伦敦和巴黎数据的查询,应用可以同时查询两台服务器,或者可以使用主库/备库复制,在每台服务器上保留其他办公室数据的只读副本。

多服务器并行查询执行

上述许多方案都允许多台服务器处理多个查询,但没有一种允许单个查询同时使用多台服务器来更快完成。这种方案允许多台服务器在同一个查询上并发工作。通常的做法是把数据分散到各台服务器上,让每台服务器执行该查询中属于自己的部分,然后把结果返回给一台中心服务器,由它汇总结果并返回给用户。这种方案也可以使用PL/Proxy工具集来实现。

还应注意,由于PostgreSQL是开源且易于扩展的,许多公司基于PostgreSQL开发出了具有独特故障转移、复制和负载均衡能力的商业闭源解决方案。这里不讨论这些方案。

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。