高可用、负载均衡和复制
1. 不同方案的比较
共享磁盘故障转移
共享磁盘故障转移避免了只使用一份数据库拷贝带来的同步开销。 它使用一个由多个服务器共享的单一磁盘阵列。
文件系统(块设备)复制
DRBD是用于 Linux 的一种流行的文件系统复制方案。
事务日志传送
温备和热备服务器能够通过读取一个预写式日志(WAL) 记录的流来保持为当前状态。如果主服务器失效, 后备服务器
拥有主服务器的几乎所有数据, 并且能够快速地被变成新的主数据库服务器。这可以是同步的或异步的, 并且只能用于整个数据库服务器。
基于触发器的主-备复制
一个主-备复制设置会把所有数据修改查询发送到主服务器。 主服务器异步地将数据修改发送给后备服务器。当主
服务器正在运行时, 后备服务器可以回答只读查询。后备服务器对数据仓库查询是一种理想的选择。
Slony-I是这种复制类型的一个例子。它使用表粒度, 并且支持多个后备服务器。因为它
会异步更新后备服务器(批量), 在故障转移时可能会有数据丢失。
基于语句的复制中间件
通过基于语句的复制中间件,一个程序拦截每一个 SQL 查询并把它发送给一个或所有服务器。 每一个服务器独立地操作。
读写查询必须被发送给所有服务器, 这样每一个服务器都能接收到任何修改。但只读查询可以被只发送给一个服务器, 这
样允许读负载在服务器之间分布。
异步多主控机复制
通过使用异步的多主控机复制, 每一个服务器独立工作并且定期与其他服务器通信来确定冲突的事务。
同步多主控机复制
在同步多主控机复制中,每一个服务器能够接受写请求,并且在每一个事务提交之前,被修改的数据会被从原始服务器传送给每一个其他服务器。
PostgreSQL不提供这种复制类型, 尽管在应用代码或中间件中可以使用PostgreSQL的两阶段提交 (PREPARE TRANSACTION和COMMIT PREPARED) 来实现这种复制。
商业方案
因为PostgreSQL是开源的并且很容易被扩展, 一些公司已经使用PostgreSQL并且创建了带有唯一故障转移、 复制和负载均衡能力的商业性的闭源方案。
数据分区
数据分区将表分开成数据集。每个集合只能被一个服务器修改。
多服务器并行查询执行
2. 日志传送后备服务器
连续归档可以被用来创建一个高可用性(HA)集群配置, 其中有一个或多个后备服务器随
时准备在主服务器失效时接管操作。 这种能力被广泛地称为温备或日志传送。
直接从一个数据库服务器移动 WAL 记录到另一台服务器通常被描述为日志传送。 PostgreSQL通过一次一文件(WAL 段) 的 WAL 记录传输实现了基于文件的日志传送。
需要注意的是日志传送是异步的,即 WAL 记录是在事务提交后才被传送。 正因为如此,在一个窗口期内如果主服务器发生灾难性的失效则会导致数据丢失, 还没有被传送的
事务将会被丢失。
基于文件的日志传送中这个数据丢失窗口的尺寸可以通过使用参数archive_timeout进行限制,它可以被设置成低至数秒。
2.1. 规划
创建主服务器和后备服务器通常是明智的,因此它们可以尽可能相似,
因此如果该特性被使用, 主、备服务器必须对表空间具有完全相同的挂载路径。 记住如果CREATETABLESPACE在主服务器
上被执行, 在命令被执行前,它所需要的任何新挂载点必须在主服务器和所有后备服务器上先创建好。
通常,不能在两个运行着不同主版本PostgreSQL的服务器之间传送日志。
2.2. 后备服务器操作
在后备模式中,服务器持续地应用从主控服务器接收到的 WAL。 后备服务器可以从一个
WAL 归档(restore_command) 或者通过一个 TCP 连接直接从主控机(流复制)读取 WAL。
后备服务器将也尝试恢复在后备集簇的pg_xlog目录中找到的 WAL。
在启动时,后备机通过恢复归档位置所有可用的 WAL 来开始, 这称为restore_command。
2.3. 为后备服务器准备主控机
在主服务器上设置连续归档到一个后备服务器可访问的归档目录。 即使主服务器垮掉该归档位置也应当是后备服务器可访问的, 即它应当位于后备服务器本身或
者另一个可信赖的服务器,而不是位于主控服务器上。
如果你想要使用流复制,在主服务器上设置认证来允许来自后备服务器的复制连接。 即创建一个角色并且在pg_hba.conf中提
供一个或多个数据库域被设置为 replication的项。还要保证在主服务器的配置文件中 max_wal_senders被设置为足够大的值。如果要使用复制
槽, 请确保max_replication_slots也被设置得足够高。
2.4. 建立一个后备服务器
要建立后备服务器,恢复从主服务器取得的基础备份。在后备服务器的集簇数据目录中创建一个恢复命令文件recovery.conf,
并且打开standby_mode。将restore_command 设置为一个从 WAL 归档中复制文件的简单命令。
如果你想要使用流复制,在primary_conninfo 中填入一个 libpq 连接字符串,其中包括主机名(或 IP 地址) 和连接到主服务
器所需的任何附加细节。如果主服务器需要一个口令用于认证, 口令也应该被指定在primary_conninfo中。
如果你正在为高性能目的建立后备服务器,像主服务器一样建立 WAL 归档、 连接和认证,
因为在故障转移后该后备服务器将作为一个主服务器工作。
recovery.conf的一个简单例子是:
standby_mode = 'on'
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
restore_command = 'cp /path/to/archive/%f %p'
archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'
2.5. 流复制
流复制允许一台后备服务器比使用基于文件的日志传送更能保持为最新的状态。后备服务器连接到主服务器,
主服务器则在 WAL 记录产生时即将它们以流式传送给后备服务器而不必等到 WAL 文件被填充。
默认情况下流复制是异步的,在这种情况下主服务器上提交一个事务与该变化在后备服务器上变得可见之间存在短暂的延迟。
流复制中,不需要archive_timeout来缩减数据丢失窗口。
在支持 keepalive 套接字选项的系统上,设置 tcp_keepalives_idle、 tcp_keepalives_interval和tcp_keepalives_count 有助于主服务器迅速地注意到一个断开的连接
设置来自后备服务器的并发连接的最大数目 (详见max_wal_senders)
当后备服务器被启动并且primary_conninfo被正确设置, 后备服务器将在重放完归档中所有可用的 WAL 文件之后连接到主服务器。
如果连接被成功建立,你将在后备服务器中看到一个 walreceiver 进程, 并且在主服务器中有一个相应的 walsender 进程。
2.5.1. 认证
设置好用于复制的访问权限非常重要,这样只有受信的用户可以读取 WAL 流, 因为很容易从 WAL 流中抽取出需要特权才能访问的信息。
推荐为复制创建一个专用的具有REPLICATION和LOGIN特权的用户账户。
复制的客户端认证由一个在database域中指定 replication的pg_hba.conf记录控制
例如, 如果后备服务器运行在主机 IP 192.168.1.100 并且用于复制的账户名为foo,管理员可以在主服务器上向 pg_hba.conf文件增加下列行:
# 允许来自 192.168.1.100 的用户 "foo" 在提供了正确的口令时作为一个
# 复制后备机连接到主控机。
# TYPE DATABASE USER ADDRESS METHOD
host replication foo 192.168.1.100/32 md5
主服务器的主机名和端口号、连接用户名和口令在recovery.conf 文件中指定。在后备服务
器上还可以在~/.pgpass文件中设置口令 (在database域中指定replication)。
例如,如果主服务器运行在主机 IP 192.168.1.50、端口 5432上,用于负值的账户名是foo, 并且口令为foopass,管理员可以在后备服务器的 recovery.conf文件中增加下列行:
# 后备机要连接到的主控机运行在主机 192.168.1.50 上,
# 端口号是 5432,连接所用的用户名是 "foo",口令是 "foopass"。
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
2.5.2. 监控
流复制的一个重要健康指标是在主服务器上产生但还没有在后备服务器上应用的 WAL 记录数。
可以用主服务器上的pg_current_xlog_location和后备服务器上的 pg_last_xlog_receive_location来检索
2.6. 复制槽
复制槽提供了一种自动化的方法来确保主控机在所有的后备机收到 WAL 段 之前不会移除它
们,并且主控机也不会移除可能导致 恢复冲突的行,即使后备机断开也是如此。
2.6.1. 查询和操纵复制槽
每个复制槽都有一个名字,名字可以包含小写字母、数字和下划线字符。
2.6.2. 配置实例
postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
要配置后备机使用这个槽,在后备机的recovery.conf中应该配置 primary_slot_name。
这里是一个简单的例子:
standby_mode = ’on’
primary_conninfo = ’host=192.168.1.50 port=5432 user=foo password=foopass’
primary_slot_name = ’node_a_slot’
2.7. 级联复制
级联复制特性允许一台后备服务器接收复制连接并且把 WAL 记录流式传送给其他后备服务器, 就像一个转发器一样。
2.8. 同步复制
PostgreSQL流复制默认是异步的。如果主服务器崩溃, 则某些已被提交的事务可能还没有被
复制到后备服务器,这会导致数据丢失。 数据的丢失量与故障转移时的复制延迟成比例。
2.8.1. 基本配置
将synchronous_commit设置为remote_write 将导致每次提交都等待后备服务器已经接收提交记录并将它写出到其自身所
在的操作系统的确认, 但并非等待数据都被刷出到后备服务器上的磁盘。
2.8.2. 性能规划
同步复制通常要求仔细地规划和放置后备服务器来保证应用能令人满意地工作。
PostgreSQL允许应用开发者通过复制来指定所要求的持久性级别。 这可以为整个系统指定,
不过它也能够为特定的用户或连接指定, 甚至还可以为单个事务指定。
2.8.3. 高可用性规划
当synchronous_commit被设置为on或remote_write时, 发生的提交将等待直至同步后备
服务器回应。如果上一个或者唯一一个后备服务器崩溃, 响应可能不会发生。
防止数据丢失的最好解决方案是确保你不会丢失你的上一个保持同步的后备服务器。
2.9. 后备服务器中的持续归档
当在备用数据库中使用连续WAL归档时,有两种不同的情况: WAL归档可以在主数据库和备用数据库之间共享,或者备用数据库可以有自己的WAL归档。
当备用数据库具有自己的WAL归档时,将archive_mode设置为 always,并且备用数据库将为每个收到的WAL段调用归档命令, 无论是从归档恢复或通过流复制。
如果archive_mode设置为on, 则在恢复或者待机模式期间不会启用归档程序。
3. 故障转移
如果主服务器失效,则后备服务器应该开始故障转移过程。
如果后备服务器失效,则不会有故障转移发生。如果后备服务器可以被重启 ,由于可重启恢复的优势,那么恢复处理也能被立即重启。
如果后备服务器不能被重启,则一个全新的后备服务器实例应该被创建。
如果主服务器失效并且后备服务器成为了新的主服务器,那么接下来旧的主服务器重启后, 你必须有一种机制来通知旧的主服务器不再成为主服务器。
要触发一台日志传送后备服务器的故障转移,运行pg_ctl promote 或者创建一个触发器文
件,其文件名和路径由recovery.conf 中的trigger_file设置指定。如果你正在规划使用
pg_ctl promote进行故障转移,trigger_file就不是必要的。 如果你正在建立只用于从主
服务器分流只读查询而不是高可用性目的的报告服务器, 你不需要提升它。
4. 日志传送的替代方法
在主服务器和后备服务器上都会发生的操作是通常的连续归档和恢复任务。 两个数据库服务器之间唯一的接触点是两者共享的 WAL 文件归档:
主服务器写这个归档,后备服务器读取这个归档。 必须要小心地保证来自独立主服务器的 WAL 归档不会混合在一起或者混淆。 如果归档只被后备操作需要,它不必很大
4.1. 实现
1. 尽可能将主系统和后背系统设置成近乎一致, 包括在同一发行级别上的两个相同的PostgreSQL拷贝。
2. 在后备服务器上建立从主系统到一个 WAL 归档目录的连续归档。 确保在主服务器上archive_mode、 archive_command和 archive_timeout被恰当地设置
3. 建立主服务器的一个基础备份, 并且把该数据载入到后备服务器。
4. 在后备服务器上开始从本地 WAL 归档的恢复,在recovery.conf 中指定一个按之前所述进行等待的restore_command
4.2. 基于记录的日志传送
也可以使用这种替代方法来实现基于记录的日志传送,不过这需要定制开发, 并且只有在一整个 WAL 文件被传送之后改变才会对热后备查询可见。
5. 热备
术语热备用来描述服务器处于归档恢复或后备模式时连接到服务器并运行只读查询的能力。
在热备模式中运行查询与正常查询操作相似,尽管如下所述存在一些用法和管理上的区别。
当hot_standby参数在一台后备服务器上被设置为真时, 一旦恢复将系统带到一个一致的状态它将开始接受连接。 所有这些连接都被限制为只读,甚至临时表都不能被写入。
在热备期间开始的事务可能发出下列命令:
• 查询访问 - SELECT、COPY TO
• 游标命令 - DECLARE、FETCH、CLOSE
• 参数 - SHOW、SET、RESET
• 事务管理命令
• BEGIN, END, ABORT, START TRANSACTION
• SAVEPOINT, RELEASE, ROLLBACK TO SAVEPOINT
• EXCEPTION块或其他内部子事务
• LOCK TABLE,不过只在下列模式之一中明确发出: ACCESS SHARE、ROW SHARE 或 ROW
EXCLUSIVE.
• 计划和资源 - PREPARE、EXECUTE、 DEALLOCATE、DISCARD
• 插件和扩展 - LOAD
在热备期间开始的事务将不会被分配一个事务 ID 并且不能被写入到系统的预写式日志。 因此,下列动作将产生错误消息:
• 数据操纵语言(DML) - INSERT、 UPDATE、DELETE、COPY FROM、 TRUNCATE。注意在恢复期间不允许导致触发器被执行的动作。 这个限制甚至被应用到临时表,因为不分配事务
ID 表行就不能被读或写, 而当前不可能在一个热备环境中分配事务 ID。
• 数据定义语言(DDL) - CREATE、 DROP、ALTER、COMMENT。 这个限制甚至被应用到临时表,因为执行这些操作会要求更新系统目录表。
• SELECT ... FOR SHARE | UPDATE, 因为不更新底层数据文件就无法取得行锁。
• SELECT语句上的能产生 DML 命令的规则。
• 显式请求一个高于ROW EXCLUSIVE MODE的模式的LOCK。
• 默认短形式的LOCK,因为它请求ACCESS EXCLUSIVE MODE。
• 显式设置非只读状态的事务管理命令:
• BEGIN READ WRITE, START TRANSACTION READ WRITE
• SET TRANSACTION READ WRITE, SET SESSION CHARACTERISTICS AS TRANSACTIONREAD WRITE
• SET transaction_read_only = off
• 两阶段提交命令 - PREPARE TRANSACTION、 COMMIT PREPARED、ROLLBACK PREPARED,因为即使只读事务也需要在准备阶段(两阶段提交中的第一个阶段)写 WAL。
• 序列更新 - nextval()、setval()
• LISTEN, UNLISTEN, NOTIFY
在正常操作中,“只读”事务被允许更新序列并且使用LISTEN、 UNLISTEN和NOTIFY, 因此热备会话在比普通只读会话对操作的限制更紧一点的限制下操作。
5.2. 处理查询冲突
主服务器和后备服务器在多方面都松散地连接在一起。 主服务器上的动作将在后备服务器上产生效果。结果是在它们之间有潜在的负作用或冲突。
最容易理解的冲突是性能:如果在主服务器上发生一次大数据量的载入, 那么这将在后备服务器上产生一个相似的 WAL 记录
流, 因而后备服务器查询可能要竞争系统资源(例如 I/O)。
突情况包括:
• 在主服务器上取得了访问排他锁,包括显式LOCK命令和多种 DDL动作,与后备查询中的表访问冲突。
• 在主服务器上删除一个表空间与使用该表空间存储临时工作文件的后备查询冲突。
• 在主服务器上删除一个数据库与在后备服务器上连接到该数据库的会话冲突。
• 从 WAL 清除记录的应用与快照仍能“看见”任意要被移除的行的后备事务冲突。
• 从 WAL 清除记录的应用与在后备服务器上访问该目标页的查询冲突,不管要被移除的数据是否为可见。
5.3. 管理员概览
如果hot_standby在postgresql.conf中被设置为 on并且存在一个recovery.conf文件,服务器将运行在热备模式。
但是,可能需要一些时间来允许热备连接, 因为在服务器完成足
够的恢复来为查询提供一个一致的状态之前, 它将不会接受连接。在此期间,尝试连接的客
户端将被一个错误消息拒绝。 要确认服务器已经可连接,要么循环地从应用尝试连接, 要
么在服务器日志中查找这些消息:
LOG: entering standby mode
... then some time later ...
LOG: consistent recovery state reached
LOG: database system is ready to accept read only connections
在主服务器上,一旦创建一个检查点,一致性信息就被记录下来。 当读取在特定时段(当在主服务器上wal_level没有被设置为
hot_standby或者logical的期间)产生的 WAL 时无法启用热备。
如果后备服务器上某些参数在主服务器上已经被改变,它们的设置将需要重新配置。对这些参数,在后备服务器上的值必须等于或者大于主服务器上的值。
如果这些参数没有被设置得足够高,后备服务器将拒绝开始。
• max_connections
• max_prepared_transactions
• max_locks_per_transaction
• max_worker_processes
管理员为max_standby_archive_delay和 max_standby_streaming_delay选择适当的设置很重要。最好的选择取决于业务的优先级。
例如如果服务器主要的任务是作为高可用服务器, 那么你将想要低延迟设置,甚至是零(尽管它是一个非常激进的设置)。 如果后备服务器的任务
是作为一个用于决策支持查询的额外服务器, 那么将其最大延迟值设置为很多小时甚至 -1(表示无限等待)可能都是可以接受的。
在恢复模式期间,下列类型的管理命令是不被接受的:
• 数据定义语言(DDL) - 如CREATE INDEX
• 特权和所有权 - GRANT、REVOKE、REASSIGN
• 维护命令 - ANALYZE、VACUUM、CLUSTER、REINDEX
5.5. 警告
热备有一些限制。这些限制很可能在未来的发行中被修复:
哈希索引上的操作目前是不被 WAL 记录的,因此重放将不会更新这些索引。
在能够取得快照之前,需要正在运行的事务的完整知识。
主服务器上的每一个检查点将产生用于后备查询的可用启动点。
在恢复尾声,由预备事务持有的AccessExclusiveLocks将要求两倍的正常锁表项 。
可序列化事务隔离级别目前在热备中不可用