打印

PostgreSQL学习文档 8.0 四

22.2. 文件系统级别的备份
另一个备份的策略是直接拷贝PostgreSQL用于存放数据库数据的文件。 我们在 Section 16.2 里解释了这些文件的位置, 不过如果你想用这个方法,你早就找到它们的位置。 你可以用自己喜欢的任何常用文件系统备份的方法,例如

tar -cf backup.tar /usr/local/pgsql/data

不过,你要受到两个限制,令这个方法不那么实用,或者至少比 pg_dump 的方法逊色一些:



为了进行有效的备份,数据库服务器必须被关闭。 象拒绝所有联接这样的折衷的方法是不行的,因为总是有一些缓冲区数据存在。 (主要因为 tar 和类似的工具在做备份的时候并不对文件系统的状态做原子快照)。 有关关闭服务器的信息可以在 Section 16.6里面找到。 不用说,你在恢复数据之前,必须关闭服务器。

如果你曾经深入了解了数据库在文件系统布局的细节,你可能试图从对应的文件或目录里备份几个表或者数据库。 这样做是没用的,因为包含在这些文件里的信息只是部分信息。还有一半信息在提交日志文件 pg_clog/*里面,它包含所有事务的提交状态。 只有拥有这些信息,表文件的信息才是可用的。当然,试图只恢复表和相关的 pg_clog 数据也是徒劳的,因为这样会把数据库集群里的所有其他没有用的表的信息都拿出来。 所以文件系统的备份只适用于一个数据库集群的完整恢复。


另外一个文件系统备份的方法是给数据目录做一个"一致的快照", 条件是文件系统支持这个功能(并且你愿意相信它是实现正确的)。 典型的过程是制作一个包含数据库的卷的"冻结快照", 然后把整个数据库目录(不仅仅是部分,见上文)从快照拷贝到备份设备, 然后释放冻结快照。这样甚至在数据库服务器在运行的时候都可以运转。 不过,这样创建的备份会把数据库文件保存在一个没有恰当关闭数据库服务器的状态下; 因此,如果你在这个备份目录下启动数据库服务器, 它就会认为数据库服务器经历过崩溃并且重放 WAL 日志。这不是个问题,只要意识到它即可(并且确信在自己的备份中包含 WAL 文件)。

如果你的数据库分布在多个卷上(比如,数据文件和 WAL 日志在不同的磁盘上),那么可能就没有任何方法获取所有卷上准确的同步冻结快照。 在你新闻这样的情况下的一致性快照的技术之前,仔细阅读你的文件系统文档。 最安全的方法是关闭数据库服务器足够长的时间,以建立所有冻结快照。

还要说明的是,文件系统备份不会比SQL转储小。恰恰相反,大多数情况下它要大。 (比如pg_dump 不用倒出索引,只是创建它们的命令。)

TOP

22.3. 在线备份以及即时恢复(PITR)
在任何时候,PostgreSQL 都在集群的数据目录的 pg_xlog/ 子目录里维护着一套预写日志(WAL)。 这些日志记录着每一次对数据库的数据文件的修改的细节。这些日志存在是为了防止崩溃:如果系统崩溃, 数据库可以通过"重放"上次检查点以来的日志记录以恢复数据库的完整性。 但是,日志的存在让它还可以用于第三种备份数据库的策略:我们可以组合文件系统备份与 WAL 文件的备份。 如果需要恢复,我们就恢复备份,然后重放备份了的WAL文件,把备份恢复到当前的时间。 这个方法对管理员来说,明显比以前的方法更复杂,但是有非常明显的优势:



在开始的时候我们不需要一个非常完美的一致的备份。任何备份内部的不一致都会被日志重放动作修改正确 (这个和崩溃恢复时发生的事情没什么区别)。因此我们不需要文件系统快照的功能, 只需要 tar 或者类似的归档工具。

因为我们可以把无限长的 WAL 文件序列连接起来,所以连续的备份简化为连续地对 WAL 文件归档来实现。 这个功能对大数据库特别有用,因为大数据库的全备份可能并不方便。

我们可没说重放 WAL 记录的时候我们必须重放到结尾。我们可以在任意点停止重放, 这样就有一个在任意时间的数据库一致的快照。因此,这个技术支持即时恢复: 我们可以把数据库恢复到你开始备份以来的任意时刻的状态。

如果我们持续把 WAL 文件序列填充给其它装载了同样的基础备份文件的机器, 我们就有了一套"热备份"系统:在任何点我们都可以启动第二台机器, 而它拥有近乎当前的数据库拷贝。


和简单的文件系统备份技术一样,这个方法只能支持整个数据库集群的恢复,而不是一个子集。 同样,它还要求大量的归档存储:基础备份量可能很大,而且忙碌的系统将生成许多兆需要备份的的 WAL 流量。 但是,它仍然时在需要高可靠性的场合下的最好的备份技术。

要想从在线备份中成功恢复,你需要一套连续的 WAL 归档文件,它们最远回朔到你开始备份的时刻。 因此,要想开始备份,你应该在开始第一次基础备份之前设置并测试你的步骤。 根据我们讨论过的归档 WAL 文件的机制。

22.3.1. 设置 WAL 归档
抽象来看,一个运行着的 PostgreSQL 系统生成一个无限长的 WAL 日志序列。 系统物理上把这个序列分隔成 WAL段文件,通常一块时 16M 字节大 (在制作 PostgreSQL 的时候可以改变其大小)。 这些段文件的名字是数值命名的,这些数值反映他们在抽取出来的 WAL 序列中的位置。 在不适用 WAL 归档的时候,系统通常只是创建几个段文件然后"循环"使用它们, 方法是把不再使用的段文件的名字重命名为更高的段编号。 系统假设那些内容比前一次检查点更老的段文件是没用的了,然后就可以循环利用。

在归档 WAL 数据的时候,我们希望在每个段文件填充满之后捕获之, 并且把这些数据在段文件被循环利用之前保存在某处。根据应用以及可用的硬件的不同, 我们可以有许多不同的方法"把数据保存在某处": 我们可以把段文件拷贝到一个 NFS 装配的目录,把它们放到另外一台机器上, 或者把它们写入磁带机里(需要保证你有办法把文件恢复为原名), 或者把它们打成包,烧录到 CD 里,或者是其它的什么方法。 为了给数据库管理员提供最大可能性的灵活性,PostgreSQL 试图不对如何归档做任何假设。取而代之的是,PostgreSQL 让管理员声明一个 shell 命令执行来拷贝一个完整的段文件到它需要去的地方。 该命令可以简单得就是一个 cp,或者它可以调用一个复杂的 shell 脚本 — 所有都由管理员决定。

所使用的 shell 命令由配置参数 archive_command 声明, 它实际上总是放在 postgresql.conf 文件里的。 在这个字串里,任何 %p 都被要归档的文件的绝对路径代替,而任何 %f 只是被文件名代替。 如果你需要在命令里嵌入一个真正的 %,写 %%。 最简单的有用命令是类似下面这样的

archive_command = 'cp -i %p /mnt/server/archivedir/%f </dev/null'
它将把 WAL 段拷贝到目录 /mnt/server/archivedir。 这个只是一个例子,并非我们建议的方法,可能不能在所有系统上都正确运行。

归档命令将在运行 PostgreSQL 服务器的同一个用户的权限下执行。 因此被归档的 WAL 文件实际上包含你的数据库里的所有东西,所以你应该确保自己的归档数据不会被别人窥探; 比如,归档到一个没有组或者全局读权限的目录里。

有一点很重要:当且仅当归档命令成功时,它才返回零。在得到一个零值结果之后, PostgreSQL 将假设该 WAL 段文件已经成功归档, 因此它稍后将被删除或者被新的数据覆盖。但是,一个非零值告诉 PostgreSQL 该文件没有被归档; 因此它会周期性的重试直到成功。

归档命令通常应该设计成拒绝覆盖已经存在的归档文件。这是一个非常重要的安全特性, 可以在管理员操作失误(比如把两个不同的服务器的输出发送到同一个归档目录)的时候保持你的归档的完整性。 我们建议你首先要测试你准备使用到归档命令,以保证它实际上不会覆盖现有的文件, 并且在这种情况下它返回非零状态。 我们发现,在这方面, cp -i 在某些平台上是正确的,而在其它平台上是不正确的。 如果选定的命令本身并不能正确处理这个问题,你应该增加一个命令预先探测归档文件是否存在。 比如,类似下面的东西。

archive_command = 'test ! -f .../%f && cp %p .../%f'
在几乎所有的 Unix 变种上都工作正确。

在设计你的归档环境都时候,请考虑一下如果归档命令不停失败会发生什么情况, 因为有些方面要求操作者的干涉,或者是归档空间不够了。 比如,如果你往磁带机上写,但是没有自动换带机,那么就有可能发生这种情况; 如果磁带满了,那就除非换磁带,否则啥事也做不了。 你应该确保人和错误条件或者人和要求操作员干涉带错误都会正确报告, 这样才能迅速解决这些问题。否则 pg_xlog/ 目录会不停地填充 WAL 段文件, 直到问题解决。

归档命令的速度并不要紧,只要它能跟上你的服务器生成 WAL 数据的平均速度即可。 即使归档进程落在了后面一点,正常的操作也会继续进行。 如果归档进程慢很多,就会增加灾难发生的时候丢失的数据量。 同时也意味着 pg_xlog/ 目录包含大量未归档的日志段文件, 并且可能最后超出了磁盘空间。我们建议你监控归档进程,确保它是按照你的意识运转的。

如果你关心能够恢复到当前即时的状态,你可能需要采取几个额外的步骤以确保当前的, 部分填充的 WAL 段也拷贝到了某些地方。这条对于生成很少 WAL 流量的服务器 (或者在运行中有松弛阶段的)特别重要,因为在一个 WAL 段文件完全填充满进而可以归档之前, 可能需要很长时间。一个处理这些的可能的方法是设置一个 cron 作业, 周期性(比如每分钟一次)地标识当前 WAL 段文件然后把它们保存到某个安全的地方。 归档的 WAL 段和保存的当前段就足够保证你可以总是恢复到当前时间的一分钟之内。 这个行为目前还不是内置于 PostgreSQL 的,因为我们不想把 archive_command 的定义复杂化,因为那样就要要求它跟踪成功归档但是却又有不同时刻含义的同一个 WAL 文件。 archive_command 只是用于处理那些不再改变的 WAL 段文件; 除了错误重试之外,对于任何给出的文件名他都只被调用一次。

在写自己的归档命令的时候,你应该假设被归档的文件最多 64 个字符长并且可以包含 ASCII 字母,数字,以及点的任意组合。 我们不必要记住原始的全路径(%p),但是有必要记住文件名(%f)。

请注意尽管 WAL 归档允许你回复任何对你的 PostgreSQL 数据库的数据做的修改, 在最初的基础备份之后,它还是不会回复对配置文件的修改(也就是说,postgresql.conf,pg_hba.conf 和 pg_ident.conf),因为这些文件都是手工编辑的,而不是通过 SQL 操作来编辑的。 所以你可能会需要把你的配置文件放在一个日常文件系统备份过程即可处理到的地方。 参阅 Section 16.4.1 获取如何重定位配置文件的知识。

22.3.2. 进行一次基础备份
进行基础备份的过程相当简单:



确保 WAL 归档打开并且可以运转。

以数据库超级用户身份连接到数据库,发出命令

SELECT pg_start_backup('label');
这里的 label 是任意你想使用的这次备份操作的唯一标识。 (一个好习惯是使用你想把备份转储文件放置的目的地的全路径。) pg_start_backup 用你的备份的信息,在你的集群目录里,创建一个备份标签文件, 叫做 backup_label。

至于你连接到集群中的那个数据库没什么关系。你可以忽略函数返回的结果; 但是如果它报告错误,那么在继续之前处理它。

执行备份,使用任何方便的文件系统工具,比如 tar 或者 cpio。 这些操作过程中既不需要关闭数据库,也不希望关闭数据库的操作。

再次以数据库超级用户身份连接数据库,然后发出命令

SELECT pg_stop_backup();
如果这个返回成功,你的工作就完成了。


我们不需要太关心在 pg_start_backup 和开始实际的备份之间开销的时间, 也不需要太关心备份结束和 pg_stop_backup 之间的时间; 几分钟的延迟不会搞砸事情。不过,你必须确保这些操作是按顺序执行的而不是重叠执行的。

要保证你的备份转储包括所有数据库集群目录里的文件(比如,/usr/local/pgsql/data)。 如果你在使用并未放置在这个目录里的表空间,也要小心地包含它们 (并且要确保你的备份转储归档符号连接是符号连接,否则,恢复会把你的表空间搞乱)。

不过,你可以在备份转储文件里省略集群目录里的 pg_xlog/ 子目录。 这个略微复杂些的动作是值得的,因为它减少了恢复的时候的错误。 如果 pg_xlog/ 是一个指向集群目录之外的一个符号连接,那么这件事情很容易处理, 出于性能考虑的时候经常这么做。

要使用这个备份,你需要保存所有备份开始以及之后的 WAL 段文件。 为了帮助你实现这个任务,pg_stop_backup 函数创建一个备份历史文件, 它马上存储到 WAL 归档区域。这个文件的名字是以你在使用备份的时候需要的第一个 WAL 段文件的名字命名的。 比如,如果开始 WAL 文件是 0000000100001234000055CD,那么备份历史文件将命名为类似 0000000100001234000055CD.007C9330.backup 这样的东西。 (这个文件名的第二部分表示在该 WAL 文件里面的准确位置,通常可以被忽略。) 一旦你安全地把备份转储文件归了档,那么你就可以删除所有那些数值名字在这个文件前面的归档的 WAL 段。 备份历史文件只是一个小的文本文件。它包含你给予 pg_start_backup 的标签字串, 以及备份的起始时间和终止时间。如果你使用这个标签来表示转储文件放在哪里, 如果需要的话,那么归档的历史文件就足够告诉你转储文件存放在哪里了。

因为你必须保留直到你最后一次基础备份的所有归档的 WAL 文件, 那么两次基础备份之间的间隔通常是根据你想在归档 WAL 文件上花多少存储空间来定的。 你还应该考虑你准备在恢复上花多少时间,如果需要恢复的话 — 系统将需要重放所有那些段, 而如果最后一次基础备份以来,时间已经很长了,那么那些动作可能会花掉好些时间。

还有一件事值得一提,那就是 pg_start_backup 函数在数据库集群目录里创建了一个叫 backup_label 的文件,它被 pg_stop_backup 删除。 这个文件当然也会作为你的备份转储文件的一部分归档。这个备份标签文件包含你给予 pg_start_backup 的标签字串, 以及 pg_start_backup 运行的时刻,以及起始 WAL 文件的名字。 如果有混淆,那么我们可以看看备份转储文件里面然后判断转储文件来自那个备份会话。

我们还可以在 postmaster 停止的时候制作一个备份转储。 在这种条件下,很明显你不能使用 pg_start_backup 或者 pg_stop_backup, 并且因此你必须靠自己的手段来跟踪备份转储文件都是那些,以及相关的 WAL 文件最远走到哪里。 通常使用上面的在线备份步骤更好些。

22.3.3. 从在线备份中恢复
好,最糟糕的事情发生了,现在你需要从备份中恢复。下面是步骤:



停止 postmaster,如果它还在运行的话。

如果你还有足够的空间,把整个集群数据目录和所有表空间拷贝到一个临时位置, 以防万一你之后还需要它们。请注意这个预防措施要求你在系统里又足够的剩余空间来现有库的保持两份拷贝。 如果你没有足够的空间,那么你至少需要把集群数据目录的 pg_xlog 子目录的内容拷贝到安全的地方, 因为它们可能包含系统宕掉的时候还没有归档的日志。

然后清理掉所有在该集群数据目录里的现存文件, 以及所有你使用的表空间里根目录下的现存文件。

从你的备份转储中恢复数据库文件。要小心用正确的所有者(数据库系统用户,而不是 root!)和权限恢复它们。 如果你使用了表空间,你可能需要核实在 pg_tblspc/ 里的符号连接都得到正确恢复。

删除任何目前还在 pg_xlog/ 里的文件;这些文件来自备份转储,因此它们可能比目前的老。 如果你就根本没有归档 pg_xlog/,那么重建之,要注意也要重建子目录 pg_xlog/archive_status/。

如果你有在步骤 2 里面保存的 WAL 段文件,那么把它们拷贝到 pg_xlog/。 (最好是拷贝它们,而不是把它们移动回来,这样即使发生了糟糕的事情,你需要重启的时候, 你也依然拥有未修改的文件。)

在集群数据目录里创建一个恢复命令文件 recovery.conf(参阅 Recovery Settings)。 你可能还需要临时修改 pg_hba.conf 以避免普通用户连接,直到你确信恢复已经正常了为止。

启动 postmaster。postmaster 将进入恢复模式并且继续读取它需要的归档的 WAL 文件。 在恢复过程完成后,postmaster 将把 recovery.conf 改名为 recovery.done (以避免不小心因后面的崩溃再次进入恢复模式)然后开始正常的数据库操作。

检查数据库的内容以确保你已经恢复到你期望的位置。 如果还没有,回到步骤 1。如果全部正常,则恢复 pg_hba.conf 成正常状态,允许你的用户登录。


所有这些操作的关键部分时设置一个恢复命令文件, 这个文件描述你希望如何恢复以及恢复应该走到哪里。 你可以使用 recovery.conf.sample(通常安装在安装目录的 share/ 子目录里)作为原型。 你必须在 recovery.conf 里面声明的一个东西是 restore_command, 它告诉系统如何拿回归档的 WAL 文件段。类似 archive_command, 这个是一个脚本命令字串。它可以包含 %f,这个变量会被需要的日志文件名替换, 以及 %p,它会被要拷贝去的日志文件的绝对路径代替。 如果需要在命令里替换真正的 %,写 %%。 最简单的有用命令是类似下面的东西

restore_command = 'cp /mnt/server/archivedir/%f %p'
这个命令将把以前归档的 WAL 段从目录 /mnt/server/archivedir 拷贝过来。 你当然可以使用某些更复杂的东西,甚至是一个要求操作者装配合适的磁带的 shell 脚本。

重要的一点是:该命令在失败的时候返回非零值。如果日志文件没有出现在规档中,那么该系统将询问该命令; 在问到的时候,它必须返回非零。这个不是错误条件。还要注意 %p 路径的基础名将和 %f 不一样; 不要认为它们是可以互换的。

在归档中找不到的 WAL 段将被认为在 pg_xlog/ 里;这样就允许使用最近没有归档的段。 但是在归档中的段将比 pg_xlog/ 中的优先。在检索归档的文件的时候,系统将不会覆盖现有的 pg_xlog/ 内容。

通常,恢复将处理所有可用的 WAL 段,因此把数据库恢复到当前时间(或者是在所给出的可用 WAL 段数目的情况下, 我们能走到的最近的地方)。但是如果你想恢复到某些以前的时刻点(比如,就在菜鸟 DBA 删除你的主要事务表之前), 那么只需要在 recovery.conf 里声明要求的停止点。你可以通过日期/时间来声明, 也可以通过特定事务 ID 的结束来声明这个停止点,我们叫做"恢复目标"。 在我们写到这些的时候,只有日期/时间选项比较有用, 因为我们没有工具来帮助你精确地标识应该使用哪个事务 ID。

注意: 请注意停止点必须在备份的终止时间之后(也就是,pg_stop_backup 的时间)。 你无法使用一个基础备份恢复到备份正在进行中的某个时刻。 (要想恢复到该时刻,你必须回到你以前的基础备份,然后从那个位置向前滚动。)

22.3.3.1. 恢复设置
这些设置只能在 recovery.conf 里面使用,并且只是在恢复的过程中起作用。 在任何之后的恢复中,你必须重新设置他们。恢复过程开始后,它们的值无法改变。


restore_command (string)
执行检索归档 WAL 文件段序列的 shell 命令。这个参数是必须的。 字串中的任何 %f 都被从归档中检索出来的文件名替换, 而任何 %p 都被替换为拷贝过去的服务器上的绝对路径。 需要在命令里嵌入真正的 % 字符时,写 %%。

有一点很重要,那就是这个命令只有在成功的时候才返回零。 系统会向这条命令询问没有在归档里出现的文件名; 在这种情况下,它必须返回非零。比如:

restore_command = 'cp /mnt/server/archivedir/%f "%p"'
restore_command = 'copy /mnt/server/archivedir/%f "%p"'  # Windows

recovery_target_time (timestamp)
这个参数声明恢复执行到达的时间戳。最多可以声明一个 recovery_target_time 或 recovery_target_xid。缺省是恢复到 WAL 日志的结尾。 精确的停止点也受 recovery_target_inclusive 影响。

recovery_target_xid (string)
这个参数声明恢复将到达的事务 ID。要注意的是,尽管事务 ID 在事务开始的时候是认为顺序的, 但是事务可以以不同的数值顺序完成。将要恢复的事务是那些在声明的这个事务之前(可以选择包括它提交的时候的)提交的。 最多可以声明一个 recovery_target_xid 或 recovery_target_time。 缺省是恢复到 WAL 日志的结尾。精确的停止点也受 recovery_target_inclusive 影响。

recovery_target_inclusive (boolean)
声明我们是否在恢复目标之后(true),还是正好在其之前(false)停止。 适用于 recovery_target_time 和 recovery_target_xid, 不管声明的是哪个。它分别表示具有准确的提交时间或者 ID 的那些(个)事务,是否将包含在恢复之中。 缺省是 true。

recovery_target_timeline (string)
声明恢复到一个特定的时间线。缺省是恢复到进行基础备份时的当时的当前时间线上。 只是在复杂的重新恢复的情况下,你才需要设置这个参数,也就是在你需要恢复到一个本身是在即时恢复之后到达的状态下, 才需要这么做。 参阅 Section 22.3.4 进行讨论。

22.3.4. 时间线
能够把数据库恢复到以前的某个时间点的能力导致了一些类似科幻小说里的时间跟踪和并行宇宙这样的复杂情况。 在数据库的最初的历史里,可能你在周二下午 5:15 删除掉了一个非常关键的表。 然后有条不紊地拿出备份,恢复到周二晚上 5:14 即时备份。在这个数据库宇宙的历史里, 你从来没有删除过那个表。但是假如你后来认识到这么干并非绝好的主意,并且想回到最初的历史中的稍后的点。 你没法这么干,因为在数据库运行的时候,它覆盖了一些 WAL 段文件的序列,这些序列就是在你希望回去的区间里的。 因此你的确需要区分在你从那些原始数据库历史生成的 WAL 中完成即时恢复之后生成的 WAL 序列。

为了处理这些问题,PostgreSQL 有个叫时间线的概念。 每次你即时恢复到一个比 WAL 序列的结尾要早的时刻,那么就创建一个新的时间线, 以表示在该次恢复之后生成的 WAL 记录。(不过,如果恢复动作一尺处理到 WAL 的结尾, 我们就不会开始一个新的时间线:我们只是扩展现有个那个。)时间线 ID 号是 WAL 段文件名的一部分, 因此新的时间线并不会覆盖以前的时间线生成的 WAL 数据。实际上我们可以归档许多不同的时间线。 虽然这些看起来像没用的特性,但它却可能常常是救命稻草。考虑一下你并不很确信应该恢复到那个时刻的情况, 这个时候你不得不做好几次试验性即时恢复然后从中找到旧历史中最好的分支。 如果没有时间线,那么这个过程可能很快就会导致无法管理的混乱。 有了时间线,你可以恢复到任意以前的状态, 包括恢复到你后来放弃的时间线分支的状态。

每当创建一个新的时间线的时候,PostgreSQL 都创建一个"时间线历史"文件, 它显示自己从那个时间线分出来,以及何时分出来的。这些历史文件是在从包含多个时间线的规党中进行恢复时, 允许系统选取正确的 WAL 段文件的必要文件。因此,它们像 WAL 段文件一样归档到 WAL 归档里。 历史文件只是很小的文本文件(不想段文件很大),所以独立地保存他们代价很小,也值得做。 如果你喜欢,你可以在历史文件里加入注释,录自己为什么设置一个时间线以及如何设置的等信息。 这样的注释会在你有厚厚一堆不同的时间线需要选择和分析的时候特别有价值。

恢复的缺省的行为时沿着与备份基础备份的同一个时间线恢复。 如果你像恢复到某些子时间线(也就是,你想回到某些本身就是在开始恢复企图之后发生的状态), 你需要在 recovery.conf 里声明目标时间线 ID。你无法恢复到比基础备份更早的时间线分支。

22.3.5. 注意
在我们写到这些的时候,在线备份技术还有几个局限。它们可能在将来的版本中修补:



在非 B-tree 索引上的操作(散列,R-tree,和 GiST 索引)目前没有用 WAL 记录日志, 所以重放就不会更新这些索引类型。我们建议的绕开方法是在完成恢复操作之后手工 REINDEX 每个这样的索引。


还要注意,目前的 WAL 格式占地非常大,因为它包含许多磁盘影像。 这么做对于崩溃恢复用途是合适的,因为我们可能需要修补部分写入的磁盘页。 但是对 PITR 操作却没必要存储如此多页面。将来开发的一个方面就是通过删除无用的页拷贝来压缩归档的 WAL 数据。

TOP

22.4. 在不同版本之间迁移
本节讨论如何把你的数据库数据从一个 PostgreSQL 版本迁移到一个更新的版本上。 软件安装步骤本身不是本节的论题;它们的细节在 Chapter 14 里。

通常,不同主版本的 PostgreSQL 之间的内部存储结构经常做改变 (第一个点后面的数字变化)。 这个情况不会在不同的子版本里面发生(第二个点后面的数字变化)。它们通常用的都是兼容的存储格式。 比如,版本 7.0.1,7.1.2 和 7.2 是不兼容的,而 7.1.1 和 7.1.2 是兼容的。如果你在兼容的版本之间升级,那你只需要简单地用新的可执行文件替换原来的并且使用原来的磁盘存储区上就行了。 否则,你需要用 pg_dump 备份你的数据,然后到新的服务器里恢复它们; 文件系统级别的备份明显是不适用的。 系统里有一些位置有错误检查,可以防止你使用不兼容的 PostgreSQL 版本的数据区, 因此就算混淆这些事情也不会造成什么损害。

我们建议你使用新版本的 pg_dump 程序, 以便利用新版本的 pg_dump 新特性和功能。 目前版本的转储程序可以支持到最老 7.0 的服务器版本。

最短停业时间(downtime)可以这样实现:把新服务器安装在不同的目录然后同时在不同端口并行运行旧的和新的服务器。 这样你就可以用类似

pg_dumpall -p 5432 | psql -d template1 -p 6543
这样的命令转移数据,或者用你选择的过渡文件。 然后你就可以关闭老服务器然后在旧端口启动新服务器。 你要确保在你运行完pg_dumpall之后没有更新旧数据库, 否则你显然会丢失那些数据。 参阅 Chapter 19 获取如何禁止访问的更多信息。

实际上你在完全切换到新库之前可能还要测试你的客户端应用。 这是另外一个设置新旧库并行安装的原因。

如果你不能或者不想同时运行两个服务器。你可以在安装新服务器之前做备份工作, 然后停掉服务器,移走旧版本的东西,安装新版本,启动服务器,恢复数据。例如:

pg_dumpall > backup
pg_ctl stop
mv /usr/local/pgsql /usr/local/pgsql.old
cd ~/postgresql-7.8.0.0
gmake install
initdb -D /usr/local/pgsql/data
postmaster -D /usr/local/pgsql/data
psql < backup
参阅 Chapter 16 ,里面有启动和停止服务器以及其他细节信息。 安装指导里面有执行这些步骤的战略位置的建议。

注意: 当你"把旧的安装移走"之后,它就可能不再可以毫无问题地使用了。 安装的某些可执行程序包含各种安装的程序和数据文件的绝对路径信息。这通常算不上什么问题, 但是如果你计划并行使用两个安装一段时间,你应该在制作的时候给它们赋不同的安装目录。 (这个问题在 PostgreSQL 8.0 和更新的版本里是可以纠正的, 不过在老版本上,你可得小心。)

TOP

Chapter 23. 监控数据库的活动
Table of Contents
23.1. 标准 Unix 工具
23.2. 统计收集器
23.2.1. 统计收集器配置
23.2.2. 查看收集到的统计信息
23.3. 查看锁
一个数据库管理员常常想知道,"现在系统正在干什么呢?"。 本章讨论如何回答这个问题。

有一些工具可以用来监控数据库的活动以及分析性能。 本章大部分内容是用于描述 PostgreSQL 的统计收集器(statistics collector), 但我们也不能忽视普通的 Unix 监控程序,比如 ps,top,iostat 和 vmstat。 同样,一旦我们找出了一个性能恶劣的查询,那么我们可能还要用 PostgreSQL 的 EXPLAIN 命令进一步分析。 Section 13.1 里讨论了 EXPLAIN 和其它用于理解独立查询的行为的方法。

23.1. 标准 Unix 工具
在大多数平台上,PostgreSQL 修改 ps 输出的命令标题,这样我们就很容易找出某个服务器进程。一个简单的显示如下

$ ps auxww | grep ^postgres
postgres   960  0.0  1.1  6104 1480 pts/1    SN   13:17   0:00 postmaster -i
postgres   963  0.0  1.1  7084 1472 pts/1    SN   13:17   0:00 postgres: stats buffer process   
postgres   965  0.0  1.1  6152 1512 pts/1    SN   13:17   0:00 postgres: stats collector process   
postgres   998  0.0  2.3  6532 2992 pts/1    SN   13:18   0:00 postgres: tgl runbug 127.0.0.1 idle
postgres  1003  0.0  2.4  6532 3128 pts/1    SN   13:19   0:00 postgres: tgl regression [local] SELECT waiting
postgres  1016  0.1  2.4  6532 3080 pts/1    SN   13:19   0:00 postgres: tgl regression [local] idle in transaction
(调用 ps 的方法因不同的平台而略有不同,显示出来的细节也有一些区别。这个例子来自一个最近的 Linux 系统。) 这里显示出来的第一个进程时 postmaster,主服务进程。 给它显示的命令参数和运行它的时候给它的是一样的。 下面两个进程实现统计收集器,我们将在下一节里详细描述它。 (如果你设置了系统不启动统计收集器,那么它们不会出现。) 剩下的进程每个都是一个服务器进程,每个处理一个客户的联接。 每个这样的进程都用下面的形式设置其命令行显示:

postgres: user database host activity
在该客户端联接的生命期中,用户,数据库,和联接源主机项都保持不变, 但是活跃性指示符会变化。活跃性可以是 idle (也就是说, 等待客户端的命令),idle in transaction (在一个BEGIN 块里等待用户),或者一个命令类型名,比如 SELECT。 同样,如果服务器目前正在等待一个其它服务器进程持有的锁的时候, 会在信息后面附加 waiting。在上面的例子中,我们可以推出:进程 1003 正在等待 1016完成其事务, 这样才能施放一些锁或者其它什么东西.

提示: Solaris 需要特别的处理。 你必需使用 /usr/ucb/ps,而不是 /bin/ps.你还必需使用两个 w 标志,而不是一个。另外,你最初调用 postmaster 时用到的命令行在 ps 状态显示中必须比 ps 给每个服务器进程显示的短。如果没满足这三个条件, 那么 ps 为每个服务器进程输出的将是最初的 postmaster 的命令行。

TOP

23.2. 统计收集器
PostgreSQL 的 统计收集器是一个支持收集和汇报服务器活跃性信息的子系统。 目前,这个收集器可以给对表和索引的访问计数,包括磁盘块的数量和独立行的项。 它还可以判断当前其它服务器进程在执行的命令是什么。

23.2.1. 统计收集器配置
因为统计收集给查询处理增加了一些过热,所以你可以把系统配置为收集信息,也可以配置为不收集信息。 这个是由配置参数控制的,这些配置参数通常在 postgresql.conf 里设置(参阅 Section 16.4 获取有关设置配置参数的细节)。

要想让统计收集器运行起来, 参数 stats_start_collector 必须设置为真。 这个设置是缺省设置,也是建议设置,但是如果你对统计不感兴趣并且想把所有过荷都挤出去, 那么可以把它设置为假。(不过,省下来的东西并不多。) 请注意这个选项在服务器运行的时候并不能改变。

参数 stats_command_string, stats_block_level,和 stats_row_level 控制实际发送给收集器的数量, 因此也决定了会产生多少运行时过热。这些选项分别决定一个服务器进程是否发送它的当前命令字串, 磁盘块层次的访问统计,以及行层次的访问统计给收集器。 通常这些参数在 postgresql.conf 中设置, 因此它们适用于所有服务器进程, 但是我们也可以在独立的会话里用 SET 命令把它们打开或者关闭。 (为避免普通用户把它们的活跃性隐藏不给管理员看,只有超级用户允许用 SET 命令修改这些参数。)

注意: 因为参数 stats_command_string, stats_block_level,和 stats_row_level 缺省时false, 索引实际上缺省配置中不收集任何统计信息。 打开这些配置变量中的一个或多个可以显著增加统计收机器生成的有用信息的数量,代价是增加了一点运行时开销。

23.2.2. 查看收集到的统计信息
有一些预定义的视图可以用于显示统计收集的结果,在 Table 23-1 里列出。另外, 我们可以使用下层的统计函数制作自己的客户化视图。

在使用统计观察当前活跃性的时候,你必须意识到这些信息并不是实时更新的。 每个独立的服务器进程只是在准备空闲的时候才向收集器传送新的块和行访问计数; 因此正在处理的查询或者事务并不影响显示出来的总数。 同样,收集器本身也时最多每 pgstat_stat_interval毫秒 (缺省是 500)发送一次新的报告。因此显示的总数总是落后于实际活动。 当前的查询信息是立即报告给收集器的,但是在其可见之前,仍然受 pgstat_stat_interval 的约束。

另外一个需要着重指出的问题是,在请求服务器进程显示任何这些统计信息的时候, 它首先抓取收集器进程发出的最新的报告。然后它就接着拿这些数据作为所有统计视图和函数的快照, 直到它当前的事务的结束。 因此统计在你当前事务的持续期间内时不会改变的。这是一个特性, 而不是一个毛病,因为这样就允许你在统计上执行几个查询并且对结果进行相关性的检查而又不用担心这些数字会背着你变化。 但是如果你想看每个查询的新的结果,那么就要记住在任何事务块外面处理这些查询。

Table 23-1. 标准统计视图

视图名字 描述
pg_stat_activity 每个服务器进程一行,显示进程ID,数据库,用户,当前查询和当前查询开始执行的时间。 只有在打开 stats_command_string 参数的时候, 才能得到报告当前查询的相关信息的各个字段。 另外,除非检查这些字段的用户是超级用户或者是拥有正在汇报的进程的同一个用户,否则它们显示为空。 (请注意因为收集器的报告延迟,当前查询只是对长时间运行的查询及时更新。)  
pg_stat_database 每个数据库一行,显示激活的后端的数量,提交的事务总数以及在该数据库中回卷数目的总数, 读取的磁盘块的总数,以及缓冲区命中的总数(也就是中所需要的块已经在缓冲区中找到,从而避免了读取块的动作)。  
pg_stat_all_tables 当前数据库中每个表一行,里面有顺序扫描和索引扫描的总数, 每种类型的扫描返回的元组的总数以及元组插入,更新,和删除的总数。  
pg_stat_sys_tables 和pg_stat_all_tables一样,只不过只显示系统表。  
pg_stat_user_tables 和pg_stat_all_tables一样,只不过只显示用户表。  
pg_stat_all_indexes 当前数据库的每个索引一行,包括使用了该索引的索引扫描总数, 索引元组读取的总数以及成功抓取的堆元组的数目(如果有些索引记录指向过期的堆元组,那么这个数目可能少一些。)  
pg_stat_sys_indexes 和pg_stat_all_indexes一样,只不过只包含那些显示为系统表上的索引。  
pg_stat_user_indexes 和pg_stat_all_indexes一样,只不过只包含那些显示为用户表上的索引。  
pg_statio_all_tables 当前数据库中每个表一行,包含从该表中读取的磁盘块总数, 缓冲区命中的总数,在该表上所有索引的磁盘块读取和缓冲区命中总数, 在该表的辅助 TOAST 表(如果存在)上的磁盘块读取和缓冲区命中总数, 以及 TOAST 表的索引的磁盘块读取和缓冲区命中总数.  
pg_statio_sys_tables 和pg_statio_all_tables一样,只不过只显示系统表.  
pg_statio_user_tables 和pg_statio_all_tables一样,只不过只显示用户表.  
pg_statio_all_indexes 当前数据库中每个索引一行,包含该索引的磁盘块读取和缓冲区命中的数目。  
pg_statio_sys_indexes 和pg_statio_all_indexes一样,只不过只显示系统表。  
pg_statio_user_indexes 和pg_statio_all_indexes一样,只不过只显示用户表。  
pg_statio_all_sequences 当前数据库中每个序列对象一个,包含该序列磁盘读取和缓冲区命中的数目。  
pg_statio_sys_sequences 和pg_statio_all_sequences一样,只不过只显示系统序列。 (准确地说,我们没有定义系统序列,所以这个视图总是空的。)  
pg_statio_user_sequences 和pg_statio_all_sequences一样,只不过只显示用户序列。  

每索引的统计对于判断哪个索引得到使用以及它们的效果非常有用。

pg_statio_ 系列视图在判断缓冲的效果的时候特别有用。 在实际磁盘读取远表缓冲命中小的时候,我们就知道这个缓冲基本满足所有读要求,因此不需要进行内核调用。 但是,这些统计并未给出所有信息:由于 PostgreSQL 处理磁盘的方式,不在 PostgreSQL 缓冲区缓存的数据可能仍然驻留在内核的 I/O 缓存中, 因此仍然可能不经过物理读取而抓取。 对获取 PostgreSQL 的 I/O 行为的更多细节感兴趣的用户可以结合使用 PostgreSQL 的统计收集器和可以分析内核 I/O 处理的操作系统工具来获取更多细节。

其它查看统计的方法可以通过书写使用下层统计访问函数的查询来设置,这些下层统计访问函数和标准视图里使用的是一样的。 这些函数在Table 23-2 中列出。 就某数据库进行访问的函数接受一个数据库 OID 为参数来标识需要报告哪个数据库。 就某表或者某索引进行访问的函数接受一个表或者索引的 OID。 (请注意这些函数只能看到在当前数据库里的表和索引)。 就某后端进行访问的函数接受一个后端 ID 号,其范围从一到当前活跃后端的数目。

Table 23-2. 统计访问函数

函数 返回类型 描述
pg_stat_get_db_numbackends(oid) integer 处理数据库的活跃的后端进程数目。  
pg_stat_get_db_xact_commit(oid) bigint 数据库中已提交事务数量。  
pg_stat_get_db_xact_rollback(oid) bigint 数据库中回卷的事务数量  
pg_stat_get_db_blocks_fetched(oid) bigint 数据库中磁盘块抓取请求总数  
pg_stat_get_db_blocks_hit(oid) bigint 为数据库在缓冲区中找到的磁盘块抓取请求总数  
pg_stat_get_numscans(oid) bigint 如果参数是一个表,那么就是进行的顺序扫描的数目, 如果是一个索引,那么就是索引扫描的数目。  
pg_stat_get_tuples_returned(oid) bigint 如果参数是一个表,那么就是顺序扫描读取的元组数目, 如果是一个索引,那么就是索引元组的数目  
pg_stat_get_tuples_fetched(oid) bigint 如果参数是一个表,那么就是顺序扫描抓取的有效(未过期)的表元组数目, 如果是一个索引,那么就是用这个索引抓取的有效表元组数目  
pg_stat_get_tuples_inserted(oid) bigint 插入表中的元组数量  
pg_stat_get_tuples_updated(oid) bigint 在表中已更新的元组数量  
pg_stat_get_tuples_deleted(oid) bigint 从表中删除的元组数量  
pg_stat_get_blocks_fetched(oid) bigint 表或者索引的磁盘块抓取请求的数量  
pg_stat_get_blocks_hit(oid) bigint 在缓冲区中找到的表或者索引的磁盘块请求数目  
pg_stat_get_backend_idset() set of integer 当前活跃后端 ID 的集合(从 1 到活跃后端的数目)。 参阅文本中的使用样例。  
pg_backend_pid() integer 附着在当前会话上的后端进程 ID  
pg_stat_get_backend_pid(integer) integer 给出的后端进程的进程号  
pg_stat_get_backend_dbid(integer) oid 指定后端进程的数据库 ID  
pg_stat_get_backend_userid(integer) oid 指定后端进程的用户 ID  
pg_stat_get_backend_activity(integer) text 后端进程的当前活跃查询(如果调用者不是超级用户,或者不是与被查询的会话同一个用户, 或者没有打开 stats_command_string 则为 NULL)  
pg_stat_get_backend_activity_start(integer) timestamp with time zone 指定后端进程当前正在执行的查询的起始时间(如果当前用户不是超级用户,或者 不是与被查询的会话同一个用户,或者没有打开 stats_command_string 则为 NULL)  
pg_stat_reset() boolean 重置所有当前收集的统计。  

注意: pg_stat_get_db_blocks_fetched 减去 pg_stat_get_db_blocks_hit 就是为该表,索引或者数据库 发出的 read() 内核调用的数目;不过实际的物理读取的数目通常比较低, 因为还有内核级的缓冲。

函数 pg_stat_get_backend_idset 提供了一个为每个活跃后端进程生成一行的便捷的方法。 比如,要显示所有后端的PID和它们的当前查询:

SELECT pg_stat_get_backend_pid(s.backendid) AS procpid,
       pg_stat_get_backend_activity(s.backendid) AS current_query
    FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s;

TOP

23.3. 查看锁
监控数据库活动的另外一个有用的工具是系统表 pg_locks。 这样就允许数据库管理员查看在锁管理器里面的未决的锁的信息。比如, 这个功能可以用于:



查看当前所有未决锁,所有在某一特定数据库里的关系上的锁, 所有在特定关系上的锁,或者某一 PostgreSQL 会话持有的所有锁。

判断当前数据库里带有最多未批准锁的关系(它很可能是数据库客户端的竞争源)。

判断锁竞争给数据库性能带来的影响,以及锁竞争随着整个数据库流量的变化所产生的变化。

pg_locks 视图的细节在 Section 41.33 里。 有关更多 PostgreSQL 的锁和管理并发性的 信息,请参考 Chapter 12。

TOP

Chapter 24. 观察磁盘使用情况
Table of Contents
24.1. 判断磁盘的使用量
24.2. 磁盘满导致的失效
本章讨论如何观察PostgreSQL数据库系统的磁盘使用情况。

24.1. 判断磁盘的使用量
每个表都有一个主堆的磁盘文件,大多数数据都存储在这里。如果一个表有着可能会很长的字段, 则另外还有一个TOAST文件与这个表相关联, 用于存储因为数值太长而不能存储在主表里面的数据(参阅 Section 49.2)。 如果有这个扩展表,那么表上会有一个TOAST索引。 当然,同时还可能有索引和基表关联。 每个表和索引都存放在单独的磁盘文件里 — 如果文件超过一吉,甚至可能多于一个文件。 这些文件的命名原则在 Section 49.1 里描述。

你可以从三个地方监视磁盘空间:从 psql 里使用 VACUUM 的信息,从 psql 里使用 contrib/dbsize 里面的工具,以及在命令行上使用 contrib/oid2name 里面的工具。如果 psql 和任何最近刚刚清理(或者分析过)的数据库进行联接, 那么我们可以用查询来查看任意表的磁盘使用:

SELECT relfilenode, relpages FROM pg_class WHERE relname = 'customer';

relfilenode | relpages
-------------+----------
       16806 |       60
(1 row)
每个页通常都是 8 K字节。(记住,relpages只是由 VACUUM, ANALYZE 和几个 DDL 命令,比如CREATE INDEX 所更新。) 如果你想直接检查表的磁盘文件,那么 relfilenode 应该有用。

要显示 TOAST 的表使用的空间,我们可以使用一个类似下面这样的查询:

SELECT relname, relpages
    FROM pg_class,
         (SELECT reltoastrelid FROM pg_class
          WHERE relname = 'customer') ss
    WHERE oid = ss.reltoastrelid
       OR oid = (SELECT reltoastidxid FROM pg_class
                 WHERE oid = ss.reltoastrelid)
    ORDER BY relname;

       relname        | relpages
----------------------+----------
pg_toast_16806       |        0
pg_toast_16806_index |        1

你也可以很容易地显示索引的用量:

SELECT c2.relname, c2.relpages
    FROM pg_class c, pg_class c2, pg_index i
    WHERE c.relname = 'customer'
        AND c.oid = i.indrelid
        AND c2.oid = i.indexrelid
    ORDER BY c2.relname;

       relname        | relpages
----------------------+----------
customer_id_indexdex |       26

我们很容易用下面的信息找出最大的表和索引:

SELECT relname, relpages FROM pg_class ORDER BY relpages DESC;

       relname        | relpages
----------------------+----------
bigtable             |     3290
customer             |     3144

contrib/dbsize 向我们的数据库里装载一些函数,这些函数 允许我们在 psql 里找出一个表或者一个数据库的大小,而且不用 VACUUM或者 ANALYZE。

你也可以使用 oid2name 显示磁盘用量。参阅 该目录中的 README.oid2name 获取例子。它包括一个为每个 数据库显示磁盘用量的脚本。

TOP

24.2. 磁盘满导致的失效
一个数据库管理员最重要的磁盘监控任务就是确保磁盘不会写满。 一个写满了的数据磁盘可能不会导致数据的崩溃,但它肯定会导致系统进一步使用的问题。 如果 WAL 文件也在同一个磁盘上(缺省配置就是这样), 会发生数据库服务器恐慌,并且停止运行。

如果你不能通过删除一些其他的东西来释放一些磁盘空间,那么 你可以通过使用表空间把一些数据库文件移动到其他文件系统上去。 参阅 Section 18.6 获取更多信息。

提示: 有些文件系统在快满的时候性能会急剧恶化,因此不要等到磁盘完全满的时候才采取行动。

如果你的系统支持每用户的磁盘份额,那么数据库将自然受制于用户所处的服务器给他的份额限制。 超过份额的负面影响和完全用光磁盘是完全一样的。

TOP

Chapter 25. 预写式日志(Write-Ahead Logging (WAL))
Table of Contents
25.1. WAL 的好处
25.2. WAL 配置
25.3. 内部
预写式日志 (WAL) 是一种实现事务日志的标准方法。有关它的详细描述可以在大多数(如果不是全部的话)有关事务处理的书中找到。 简而言之,WAL 的中心思想是对数据文件的修改(它们是表和索引的载体)必须是只能发生在这些修改已经记录了日志之后, 也就是说,在描述这些变化的日志记录冲刷到永久存储器之后。 如果我们遵循这个过程,那么我们就不需要在每次事务提交的时候都把数据页冲刷到磁盘,因为我们知道在出现崩溃的情况下, 我们可以用日志来恢复数据库:任何尚未附加到数据页的记录都将先从日志记录中重做(这叫向前滚动恢复,也叫做 REDO)。

25.1. WAL 的好处
使用 WAL 的第一个主要的好处就是显著地减少了磁盘写的次数。 因为在日志提交的时候只有日志文件需要冲刷到磁盘;而不是事务修改的所有数据文件。 在多用户环境里,许多事务的提交可以用日志文件的一次 fsync() 来完成。而且,日志文件是顺序写的, 因此同步日志的开销要远比同步数据页的开销要小。 这一点对于许多小事务修改数据存储的许多不同的位置更是如此。

另外一个好处就是数据页的完整性。实际情况是,在 WAL 之前,PostgreSQL 从来不能保证在崩溃的情况下数据页的完整性。 在 WAL 之前,在写的过程中的任何崩溃都可能导致:



索引记录指向一个不存在的表的行

索引记录在分裂操作中丢失

完全崩溃了的表和索引页的内容,因为数据页只写了一部分

索引的问题(问题 1 和 2)可能已经通过额外的 fsync 调用修补好了,但是如果没有 WAL,那么没有很明显的处理第三种情况的方法; WAL 在日志里保存整个数据页的内容 -- 如果那些内容在崩溃后的恢复中需要确保数据页的完整性的话。

最后,WAL 还提供了数据库在线备份和恢复(backup and restore (BAR))的可能, 就像 Section 22.3 里描述的那样。 通过归档的 WAL 文件,我们可以支持恢复到手头的 WAL 文件包含的任意时刻: 我们只需要简单地安装以前的数据库的物理备份,然后重放 WAL 到自己希望的时间。 另外,物理备份还不必是数据库状态的一个即时快照 — 如果它是花了一段时间制作的话, 因为 WAL 日志的重放将修复任何内部的不一致。

TOP

25.2. WAL 配置
有几个与 WAL 相关的参数会影响数据库性能。 本节讨论它们的使用。参阅 Section 16.4 获取有关服务器配置参数的一般信息。

检查点(Checkpoints) 是事务序列中的点, 我们保证在该点之前的所有日志信息都更新到数据文件中去了。 在检查点时,所有脏数据页都冲刷到磁盘并且向日志文件中写入一条特殊的检查点记录。 结果是,在发生崩溃的时候,恢复器就知道应该从日志中的哪个点(称做 redo 记录)开始做 REDO 操作, 因为在该记录前的对数据文件的任何修改都已经在磁盘上了。 在完成检查点处理之后,任何在 redo 记录之前写的日志段都不再需要, 因此可以循环使用或者删除。(在进行 WAL 归档的时候, 这些日志在循环利用或者删除之前必须先归档。)

服务器的后端写进程将每隔这段时间就自动执行一个检查点。 每隔 checkpoint_segments 个日志段就创建一个检查点, 或者每隔 checkpoint_timeout 秒创建一个。 以先到为准。缺省设置分别是 3 个段和 300 秒。 我们也可以用 SQL 命令 CHECKPOINT 强制一个检查点。

减少 checkpoint_segments 和/或 checkpoint_timeout 会令检查点更频繁一些。 这样就允许更快的崩溃后恢复(因为需要重做的工作更少)。不过, 我们必须在这个目的和更频繁地冲刷脏数据页所带来的额外开销之间取得平衡。 另外,为了保证数据页的一致性,在每个检查点之后的第一次数据页的变化会导致对整个页面内容的日志记录。 因此,检查点时间间隔短了会导致输出到 WAL 日志中的数据的增加,会抵销一部分缩短间隔的目标, 并且怎么着都会产生更多的磁盘 I/O。

检查点开销相当高,首先是因为它需要写出所有当前脏的缓冲区,其实是因为他们导致上面讨论的额外的后继 WAL 流量。 因此把检查点参数设置得足够高,让检查点发生的频率降低是明智的。要对你的检查点参数的一个简单自检,可以设置 checkpoint_warning 参数。如果检查点发生的间隔接近 checkpoint_warning 秒, 那么将向服务器日志输出一条消息,建议你增加 checkpoint_segments 的数值。 偶尔出现的这样的警告并不会导致警告,但是如果它出现得太频繁,那么就应该增加检查点控制参数。

至少会有一个 WAL 段文件,而且通常不会超过 2 * checkpoint_segments + 1 个文件。每个段文件通常 16MB 大(当然你可以在制作服务器的时候修改它)。你可以用这些信息来估计 WAL 需要的空间。 通常,如果一个旧的日志段文件不再需要了,那么它将得到循环使用(重命名为顺序的下一个可用段)。 如果由于短期的日志输出峰值,导致了超过 2 * checkpoint_segments + 1 个段文件, 那么到系统再次回到这个限制之内的时候,多于的段文件会被删除,而不是循环使用。

有两个常用的 WAL 函数: LogInsert 和 LogFlush。 LogInsert 用于向共享内存中的 WAL 缓冲区里加一条新的记录。如果没有空间存放新记录, 那么LogInsert 就不得不写出(向内核缓存里写)一些填满了的WAL缓冲。 我们可不想这样,因为 LogInsert 用于每次数据库低层修改(比如,记录插入), 都要花在受影响的数据页上持有一个排它锁的时间,因为该操作需要越快越好;更糟糕的是, 写 WAL 缓冲可能还会强制创建新的日志段, 它花的时间甚至更多。通常,WAL 缓冲区应该由一个 LogFlush 请求来写和冲刷, 在大部分时候它都是发生在事务提交的时候以确保事务记录被冲刷到永久存储器上去了。在那些日志输入量比较大的系统上, LogFlush 请求可能不够频繁,这样就不能避免 LogInsert 进行写操作。在这样的系统上,我们应该通过修改配置参数 wal_buffers 的值来增加 WAL 缓冲区的数量。缺省的 WAL 缓冲区数量是 8。增加这个数值将造成对应的共享内存使用量的增加。 (要注意的是,目前我们没有什么证据表明把 wal_buffers 的设置增大超过缺省是值得的。)

commit_delay 定义了后端在使用 LogInsert 向日志中写了一条已提交的记录之后, 在执行一次 LogFlush 之前休眠的毫秒数。 这样的延迟可以允许其它的后端把它们提交的记录追加到日志中,这样就可以用一次日志同步把所有日志冲刷到日志中。 如果没有打开fsync或者当前少于 commit_siblings 个其它后端处于活跃事务状态的时候则不会发生休眠; 这样就避免了在其它事务一时半会不会提交的情况下睡眠。 请注意在大多数平台上,休眠要求的分辩率是十毫秒, 所以任何介于 1 和 10000 微秒之间的非零 commit_delay 的作用都是一样的。 适用这些参数的比较好的数值还不太清楚;我们鼓励你多做试验。

wal_sync_method 参数决定PostgreSQL 如何请求内核强制将 WAL 更新输出到磁盘。只要满足可靠性,那么所有选项应该都是一样的,但是哪个最快则可能和平台密切相关。 请注意如果你关闭了 fsync,那么这个参数就无所谓了。

打开 wal_debug 配置参数(前提是 PostgreSQL编译的时候打开了这个支持) 将导致每次 LogInsert 和 LogFlush WAL 调用都被记录到服务器日志。这个选项以后可能会被更通用的机制取代。

TOP


感谢一直以来您对我们的支持!
当前时区 GMT+8, 现在时间是 2008-10-16 11:33 京ICP证060528 号

Designed By 17DST