预写式⽇志(Write-AheadLogging(WAL))
预写式⽇志(WAL)是⼀种实现事务⽇志的标准⽅法。有关它的详细描述可以在⼤多数(如果不是全部的话)有关事务处理的书中到。简⽽⾔之,WAL 的中⼼思想是对数据⽂件的修改(它们是表和索引的载体)必须是只能发⽣在这些修改已经记录了⽇志之后,也就是说,在描述这些变化的⽇志记录冲刷到永久存储器之后。如果我们遵循这个过程,那么我们就不需要在每次事务提交的时候都把数据页冲刷到磁盘,因为我们知道在出现崩溃的情况下,我们可以⽤⽇志来恢复数据库:任何尚未附加到数据页的记录都将先从⽇志记录中重做(这叫向前滚动恢复,也叫做 REDO)。
25.1. WAL的好处
使⽤ WAL 的第⼀个主要的好处就是显著地减少了磁盘写的次数。因为在⽇志提交的时候只有⽇志⽂件需要冲刷到磁盘;⽽不是事务修改的所有数据⽂件。在多⽤户环境⾥,许多事务的提交可以⽤⽇志⽂件的⼀次 fsync() 来完成。⽽且,⽇志⽂件是顺序写的,因此同步⽇志的开销要远⽐同步数据页的开销要⼩。这⼀点对于许多⼩事务修改数据存储的许多不同的位置更是如此。
另外⼀个好处就是数据页的完整性。实际情况是,在 WAL 之前,PostgreSQL 从来不能保证在崩溃的情况下数据页的完整性。在 WAL 之前,在写的过程中的任何崩溃都可能导致:
1. 索引记录指向⼀个不存在的表的⾏
2. 索引记录在分裂操作中丢失
3. 完全崩溃了的表和索引页的内容,因为数据页只写了⼀部分
索引的问题(问题 1 和 2)可能已经通过额外的 fsync 调⽤修补好了,但是如果没有 WAL,那么没有很明显的处理第三种情况的⽅
法; WAL 在⽇志⾥保存整个数据页的内容 -- 如果那些内容在崩溃后的恢复中需要确保数据页的完整性的话。
最后,WAL 还提供了数据库在线备份和恢复(backup and restore (BAR))的可能,就像⾥描述的那样。通过归档的 WAL ⽂件,我们可以⽀持恢复到⼿头的 WAL ⽂件包含的任意时刻:我们只需要简单地安装以前的数据库的物理备份,然后重放 WAL 到⾃⼰希望的时间。另外,物理备份还不必是数据库状态的⼀个即时快照 — 如果它是花了⼀段时间制作的话,因为 WAL ⽇志的重放将修复任何内部的不⼀致。提交更改是内存条吗
25.2. WAL配置
有⼏个与 WAL 相关的参数会影响数据库性能。本节讨论它们的使⽤。参阅获取有关服务器配置参数的⼀般信息。
检查点(Checkpoints)是事务序列中的点,我们保证在该点之前的所有⽇志信息都更新到数据⽂件中去了。在检查点时,所有脏数据页都冲刷到磁盘并且向⽇志⽂件中写⼊⼀条特殊的检查点记录。结果是,在发⽣崩溃的时候,恢复器就知道应该从⽇志中的哪个点(称做redo 记录)开始做 REDO 操作,因为在该记录前的对数据⽂件的任何修改都已经在磁盘上了。在完成检查点处理之后,任何在 redo 记录之前写的⽇志段都不再需要,因此可以循环使⽤或者删除。(在进⾏ WAL 归档的时候,这些⽇志在循环利⽤或者删除之前必须先归档。)
服务器的后端写进程将每隔这段时间就⾃动执⾏⼀个检查点。每隔个⽇志段就创建⼀个检查点,或者每隔秒创建⼀个。以先到为准。缺省设置分别是 3 个段和 300 秒。我们也可以⽤ SQL 命令 CHECKPOINT 强制⼀个检查点。
减少 checkpoint_segments 和/或 checkpoint_timeout 会令检查点更频繁⼀些。这样就允许更快的崩溃后恢复(因为需要重做的⼯作更少)。不过,我们必须在这个⽬的和更频繁地冲刷脏数据页所带来的额外开销之间取得平衡。另外,为了保证数据页的⼀致性,在每个检查点之后的第⼀次数据页的变化会导致对整个页⾯内容的⽇志记录。因此,检查点时间间隔短了会导致输出到 WAL ⽇志中的数据的增加,会抵销⼀部分缩短间隔的⽬标,并且怎么着都会产⽣更多的磁盘 I/O。
检查点开销相当⾼,⾸先是因为它需要写出所有当前脏的缓冲区,其实是因为他们导致上⾯讨论的额
外的后继 WAL 流量。因此把检查点参数设置得⾜够⾼,让检查点发⽣的频率降低是明智的。要对你的检查点参数的⼀个简单⾃检,可以设置参数。如果检查点发⽣的间隔接
近 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 缓冲区的数量。缺省的 WAL 缓冲区数量是 8。增加这个数值将造成对应的共享内存使⽤量的增加。(要注意的是,⽬前我们没有什么证据表明把 wal_buffers 的设置增⼤超过缺省是值得的。)
定义了后端在使⽤ LogInsert 向⽇志中写了⼀条已提交的记录之后,在执⾏⼀次 LogFlush 之前休眠的毫秒数。这样的延迟可以允许其它的后端把它们提交的记录追加到⽇志中,这样就可以⽤⼀次⽇志同步把所有⽇志冲刷到⽇志中。如果没有打开fsync或者当前少于个其它后端处于活跃事务状态的时候则不会发⽣休眠;这样就避免了在其它事务⼀时半会不会提交的情况下睡眠。请注意在⼤多数平台上,休眠要求的分辩率是⼗毫秒,所以任何介于 1 和 10000 微秒之间的⾮零 commit_delay 的作⽤都是⼀样的。适⽤这些参数的⽐较好的数值还不太清楚;我们⿎励你多做试验。
参数决定PostgreSQL 如何请求内核强制将 WAL 更新输出到磁盘。只要满⾜可靠性,那么所有选项应该都是⼀样的,但是哪个最快则可能和平台密切相关。请注意如果你关闭了 fsync,那么这个参数就⽆所谓了。
打开配置参数(前提是 PostgreSQL编译的时候打开了这个⽀持)将导致每次 LogInsert 和 LogFlush WAL 调⽤都被记录到服务器⽇志。这个选项以后可能会被更通⽤的机制取代。
5.3. 内部
在版本 7.1 以后,WAL 是⾃动打开的。除了要求⼀些磁盘空间存放 WAL⽇志以及⼀些必要的调节以外(参阅),对管理员没有什么其他要求,
WAL ⽇志存放在数据⽬录的 pg_xlog ⽬录⾥,它是作为⼀个⽂件段的集合存储的,通常每个段 16 MB ⼤。每个段分割成多个页,通常 8K ⼤。⽇志记录头在 access/xlog.h ⾥描述;⽇志内容取决于它记录的事件的类型。段⽂件的名字是递增⾃然数,
从 000000010000000000000000 开始。⽬前这些数字不能循环使⽤,不过要把所有可⽤的数字都⽤光也需要⾮常长的时间。
WAL 的缓冲区和控制结构在共享内存⾥,并且由后端操纵;它们是⽤轻量的锁保护的。对共享内存的需求由缓冲区数量决定。缺省
的 WAL 缓冲区⼤⼩是 8 个 8 KB 的缓冲区,也就是 64KB。
⽇志位于和主数据库⽂件不同的另外⼀个磁盘上会⽐较好。你可以通过把pg_xlog⽬录移动到另外⼀个位置( postmaster 当然得关闭),然后在$PGDATA⾥原来的位置创建⼀个指向新位置的符号链接来实现。
WAL 的⽬的是确保在数据库记录被修改之前,先写了⽇志,但是这个⽬的有可能被那些向内核谎报成功写的磁盘驱动器破坏,这时候,它们实际上只是缓冲了数据⽽并未把数据存储到磁盘上。这种情况下的电源失效仍然可能导致不可恢复的数据崩溃;管理员应该确保保
存 PostgreSQL 的⽇志⽂件的磁盘不会做这种虚假汇报。
在完成⼀个检查点并且⽇志⽂件冲刷了之后,检查点的位置保存在了⽂件 pg_control ⾥。因此在需要做恢复的时候,后端⾸先读
取 pg_control 和检查点记录;然后它通过从检查点记录⾥标识的⽇志位置开始向前扫描执⾏ REDO 操作。因为数据页的所有内容都保存在检查点之后的第⼀个页⾯修改的⽇志⾥,所以⾃检查点以来的所有变化都将被恢复到⼀个⼀致的状态。
但是为了处理 pg_control 可能的损坏,我们实际上应该实现对现存的⽇志段的反向读取顺序 -- 从最新到最⽼ -- 这样才能到最后的检查点。这些还没有实现。 pg_control 很⼩(⽐⼀个磁盘页⼩),因此它出现只写了⼀部分的问题的概率⼏乎为零,到⽬前为⽌,我们还没有看到只是说不能读取 pg_control ⾃⾝的错误。因此,尽管这在理论上是⼀个薄弱环节,但是 pg_control 看起来似乎并不是实际会发⽣的问题。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。