到目前为止,我们已经处理了基于文件的复制(或日志传送)和简单的基于流复制的设置。在两种情况中,在master上事务被提交之后,数据被提交,由slave接收。在master提交和slave实际上完全地接收到数据这段时间,它仍然会丢失。
在本章中,我们将学习如下主题:
• 确保没有任何事务丢失
• 配置PostgreSQL同步复制
• 理解并使用application_name
• 同步复制的性能影响
• 复制的速度优化
5.1 设置同步复制
正如前面提到的,同步复制已经被用来尽一切花费来保护您数据。同步复制的核心思想是在master给客户端返回成功之前,事务必须至少存在于两个服务器之上。设置同步复制工作就像设置异步复制。只是极少数在本章讨论的参数必须被改变来享受同步复制的祝福。但是,如果您想要在同步复制的基础上创建您自己的设置,我们建议从异步设置开始,并逐步扩展您的配置来把它变成同步复制。这将允许您更容易地调试系统,并且可以避免这个过程中的好多问题。
5.1.1 理解同步复制的缺点
您必须要了解的关于同步复制的最重要的事情是它是相当昂贵的。您还记得第一章的关于CAP的理论,关于光速,等等?同步复制和它的缺点是为什么我们决定将所有这些背景信息包含在这本书中的核心原因之一。理解同步复制的物理限制是必要的,否则您可能最终身陷困境。
当设置同步复制时,尽量记住以下几点:
• 最小化延迟
• 确保您有冗余延迟
• 同步复制比异步复制代价更高
5.1.2 理解application_name参数
application_name 在同步设置中承担着一个重要角色。在典型的应用中,人们处于调试的目的使用application_name 参数。它可以帮助您追踪bug,确定一个应用程序在做什么,等等:
test=# SHOW application_name;
application_name
——————
psql
(1 row)
test=# SET application_name TO ‘whatever’;
SET
test=# SHOW application_name;
application_name
——————
whatever
(1 row)
正如您所看到的,自由地设置application_name参数是可能的。这个设置对于我们所在的这个会话是可用的,只要我们一断开连接就会消失。现在的问题是:在同步复制中application_name必须做什么?
接下来,故事是这样的:如果slave通过流连接到master,它发送一个application_name 作为 primary_conninfo 设置的一部分。如果这个application_name 正好是 synchronous_standby_names 的第一个条目,slave将成为一个同步服务器。
[在级联复制情况下(这意味着一个slave再一次被连接到一个slave),级联的slave不会再被同步地看待。]
请记住所有这些信息,我们可以继续向前走,并配置我们的第一个同步复制。
5.1.3 使得同步复制工作
为了给您显示同步复制是如何工作的,本章将包括一个完全的工作例子,列出所有相关的配置参数。
必须在master上做几个改变。master上的postgresql.conf文件将需要如下设置:
wal_level = hot_standby
max_wal_senders = 5 # or any number
synchronous_standby_names = ‘book_sample’
hot_standby = on
# on the slave to make it
然后,我们必须配置pg_hba.conf正如我们已经在前面章节说看到的。之后,服务器可以重新启动,master准备行动。
[我们建议也设置wal_keep_segments来在master数据库上保持更多的事务日志。这使得整个设置更加健壮。]
下一步,我们可以执行一个基础备份,正如我们之前已经做过的。我们必须在slave上调用pg_basebackup。理论上,做基础备份的时候(–xlog-method=stream)我们已经包括了事务日志。这允许我们快速地启动服务器,并且没有任何较大的风险。
[–xlog-method=stream 和 wal_keep_segments 是一个很好的组合,并且在大多数情况下,在我们看来,是设置完美并安全地工作的系统的保证。]
我们已经建议在master上设置hot_standby;config文件无论如何都会被复制,因此,您可以省一步到postgresql.conf中更改此设置。当然,这不是艺术,而是一种方便,实用的方法。
一旦基础备份完成。我们就可以向前迈进,写一个简单的recovery.conf文件适用于同步复制。
iMac:slavehs$ cat recovery.conf
primary_conninfo = ‘host=localhost
application_name=book_sample
port=5432′
standby_mode = on
config文件看起来和以前的一样。唯一不同的是,我们已经添加了application_name。注意application_name参数必须和master上synchronous_standby_names的设置一样。
一旦我们写完了recovery.conf,我们就可以启动slave了。在我们的例子中,slave和master在同一台服务器上。在这种情况下,您必须确保这两个实例采用不同的TCP端口。否则第二个启动的实例将不能启动。端口可以很容易地在postgresql.conf中改变。
完成这些步骤之后,数据库实例就可以启动了。slave将检查它的连接信息并连接到master。一旦它重放所有相关的事务日志,它将处于同步状态;master和slave从那时起将保持完全相同的数据。
5.1.4 检查复制
现在我们已经启动的数据库实例,我们可以连接到系统看看系统是否正常地工作。
要检查复制,我们可以连接到master看一看pg_stat_replication。对于这个检查,我们可以连接到我们(master)实例任何数据库内部。
postgres=# \x
Expanded display is on.
postgres=# SELECT * FROM pg_stat_replication;
-[ RECORD 1 ]—-+——————————
pid | 62871
usesysid | 10
usename | hs
application_name | book_sample
client_addr | ::1
client_hostname |
client_port | 59235
backend_start | 2013-03-29 14:53:52.352741+01
state | streaming
sent_location | 0/30001E8
write_location | 0/30001E8
flush_location | 0/30001E8
replay_location | 0/30001E8
sync_priority | 1
sync_state | sync
这个系统视图将为每个连接到您的master系统的slave显示一行。
[\x会让输出对你来说更可读。如果您不使用\x来变换输出,这些行会很长,对您来说理解这个表的内容会相当困难。在扩展显示模式中,每一列将成为一行。]
您可以看到,application_name参数已经被从slave(在我们的例子中book_sample)从连接字符串传递给了master。由于application_name参数与master的synchronous_standby_names设置相匹配,我们已经使系统同步地复制了;不会再有任何事务会丢失,因为每个事务都将会瞬间存在于两个服务器上。sync_state设置会告诉您数据是如何从master移动到slave上的。
[您也可以在synchronous_standby_names中使用一系列application_names或只是一个*以指示第一个slave必须是同步的。]
5.1.5 理解性能问题
在本书中的不同观点中我们已经能够指出,同步复制是一件昂贵的事情。请记住,我们必须等待一台远端的服务器,不仅仅是本地的系统;这两个节点之间的网络需要加速。写入到不只一个节点总是比只写入一个节点更昂贵。因此,我们一定要盯紧速度,否则您可能会面临一些讨厌的意外。
[想一下我们在本书中前面的CAP理论学到了什么;带有严重的物理限制的同步复制对性能造成的影响是相当明显的。]
您真的需要问您自己的主要的问题是:您真的希望同步地复制所有的事务吗?在很多情况下,您不需要。为了证明我们的观点,让我们想象一个典型的场景:一家银行想要存储会计相关的数据以及一些日志数据。我们绝不会希望因为数据库节点出现故障而失去几百万。这种数据库可能是值得的,我们可以同步地复制它。然而,日志数据是完全不同的。它太昂贵而不能用同步复制来应对。所以,我们想以异步的方式复制这些数据来确保最大的吞吐量。
我们该如何配置一个系统,以处理重要的以及不重要的事务?答案就在您在本书的前面已经看到的一个变量上:synchronous_commit变量。
5.1.6 设置synchronous_commit 为 on
在默认的PostgreSQL配置中,synchronous_commit已经被设置为on。
在这种情况下,提交将会等待直到当前同步的standby表明它已经收到事务的提交记录并把它刷新到磁盘。换句话说,两台服务器必须报告数据已经安全地写入。除非两台服务器同时崩溃,您的数据将免于潜在的问题。(两台服务器同时崩溃是不可能)。
设置synchronous_commit为 remote_write
刷新到两个磁盘是非常昂贵的。在许多情况下,知道远程服务器已经接收了XLOG并把它传递到slave的操作系统而不刷新到磁盘已经足够了,正如我们以可以肯定的是,我们不会同时失去两台服务器,这是在性能和一致性数据保护之间的一个合理的折中。
设置synchronous_commit 为off
在前面的章节中我们已经处理过这个设置。这个想法是延迟WAL写,以减少磁盘刷新。如果性能比耐久性重要,它可以被使用。在复制的情况下,这意味着我们没有以一种完全同步的方法来执行复制。
请记住,这可能对您的应用产生严重的影响。想象 master 上的事务提交和您想要立即查询在 slave 上的数据。在您实际得到过期的数据期间仍然还有一个小窗口。
设置 synchronous_commit 为 local
local 将会本地刷新而不会等待复制的回应。换句话说,它将把您的事务转换为异步事务。把synchronous_commit设置为local,会引起一个时间窗口,在这个窗口期间slave会返回稍显陈旧的数据。 当您决定卸载读取的slave时,您应该记住这一现象。简而言之,如果您想同步地复制,您必须确保synchronous_commit 要么设置为 on 或设置为 remote_write。
在系统运行时更改耐久性设置
在系统运行时更改数据复制的方式是很容易的。在本章中,我们已经通过调整synchronous_standby_names (master)和application_name (slave)参数
建立了一个完全同步的复制基础架构。PostgreSQL的好处是,您可以在系统运行时改变您耐久性要求:
test=# BEGIN;
BEGIN
test=# CREATE TABLE t_test (id int4);
CREATE TABLE
test=# SET synchronous_commit TO local;
SET
test=# \x
Expanded display is on.
test=# SELECT * FROM pg_stat_replication;
-[ RECORD 1 ]—-+——————————
pid | 62871
usesysid | 10
usename | hs
application_name | book_sample
client_addr | ::1
client_hostname |
client_port | 59235
backend_start | 2013-03-29 14:53:52.352741+01
state | streaming
sent_location | 0/3026258
write_location | 0/3026258
flush_location | 0/3026258
replay_location | 0/3026258
sync_priority | 1
sync_state | sync
test=# COMMIT;
COMMIT
在这个例子当中,我们已经在系统运行的过程中改变了系统的耐久性。它将确保这个特殊的事务不会等待slave刷新磁盘。注意,正如您所见,sync_state没有改变。不要被您在这里看到东西愚弄;您可以完全依靠本节所述的行为。PostgreSQL完全能够单独处理每一个事务。这是这个奇妙的开源数据库独特的特征;它使您可以控制并让您决定使用哪一种耐久性要求。