本文主要是介绍PostgreSQL教程(三十三):服务器管理(十五)之可靠性和预写式日志,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本章解释预写式日志如何用于获得有效的、可靠的操作。
一、 可靠性
可靠性是任何严肃的数据库系统的重要属性,PostgreSQL尽一切可能来保证可靠的操作。可靠的操作的一个方面是,被一个提交事务记录的所有数据应该被存储在一个非易失的区域, 这样就不会因为失去电力、操作系统失败以及硬件失败(当然,除了非易失区域自身失效之外)等原因导致的数据丢失。 向计算机的永久存储(磁盘驱动器或者等效的设备)成功写入数据通常可以满足这个要求。 实际上,即使计算机受到致命损坏,只要磁盘驱动器幸存下来,那么它们就可以被移动到另外一台具有类似硬件的计算机上, 而所有已经提交的事务将保持原状。
周期地强制数据进入磁盘盘片看上去像一件简单的操作,但实际上并不是。 因为磁盘驱动器比内存和CPU要慢很多,在计算机的主存和磁盘盘片之间存在多层的高速缓存。 首先,有操作系统的高速缓存,它缓冲常用的磁盘块并且组合对磁盘的写入。 幸运的是,所有操作系统都给予应用一种强制从高速缓存写入磁盘的方法,PostgreSQL则使用了那个特性(参阅wal_sync_method参数调节如何完成之)。
然后,在磁盘驱动器的控制器上可能还有一个高速缓存;这在RAID控制卡上是特别常见的。有些高速缓存是直写式的,即写入动作在到达的时候就立刻写入到磁盘上。其它是回写式的, 即发送给驱动器的数据在稍后的某个时间写入驱动器。这样的高速缓存可能会称为可靠性灾难,因为磁盘控制器高速缓存的内存是易失性的,在发生电力失败的情况下会丢失其内容。 好一些的控制器卡有后备电池单元(BBU), 即这种卡上面有电池可以在系统电力失败的情况下提供电力。 在电力恢复之后,这些数据将会被写入磁盘驱动器。
最后,大多数磁盘驱动器都有高速缓存。有些是直写的,有些是回写的, 和磁盘控制器一样,回写的磁盘高速缓存也存在数据丢失的问题。 消费级别的IDE和SATA驱动器尤其可能包含回写式高速缓存,在掉电的情况下很容易丢失数据。很多固态驱动器(SSD)也具有易失性回写式高速缓存。
这些高速缓存通常可以被禁用,但是不同的操作系统和驱动器类型有不同的做法:
-
在Linux上,可以使用
hdparm -I
查询IDE和SATA驱动器,如果在Write cache
之后有一个*
则表示写高速缓存被启用。可以用hdparm -W 0
来关闭写高速缓存。可以使用sdparm查询SCSI驱动器。使用sdparm --get=WCE
来检查写高速缓存是否被启用,而sdparm --clear=WCE
可以用来禁用它。 -
在FreeBSD上,IDE驱动器可以使用
atacontrol
查询,而写高速缓存可以用/boot/loader.conf
中的hw.ata.wc=0
关闭。SCSI驱动器可以使用camcontrol identify
查询,而写高速缓存的查询和更改都可以使用sdparm
。 -
在Solaris上,磁盘的写高速缓存被
format -e
控制(Solaris的ZFS文件系统对于开启的磁盘写高速缓存是安全的,因为它会发出它自己的磁盘高速缓存刷写命令)。 -
在Windows上,如果
wal_sync_method
是open_datasync
(默认值),写高速缓存可以通过取消选中My Computer\Open\
禁用。另一种方法可以通过设置disk drive
\Properties\Hardware\Properties\Policies\Enable write caching on the diskwal_sync_method
为fsync
或fsync_writethrough
来阻止写高速缓存。 -
在macOS上,通过设置
wal_sync_method
为fsync_writethrough
可以阻止写高速缓存。
最近的SATA驱动器(遵循ATAPI-6及更新标准)提供了一个驱动器高速缓存刷写命令(FLUSH CACHE EXT
),而SCSI驱动器有一个存在很长时间的类似命令SYNCHRONIZE CACHE
。这些命令对于PostgreSQL并不能直接访问,但某些文件系统(例如ZFS、ext4)可以使用它们将数据刷写到回写式驱动器的盘片上。不幸的是,这些文件系统在和后备电池单元(BBU)一起工作时的表现要略差。在这种设置下,同步命令强制所有来自控制器高速缓存的数据到磁盘,消除了BBU的很多好处。你可以运行pg_test_fsync程序来看你是否被影响。如果你被影响了,BBU带来的性能好处可以通过关闭文件系统的写障碍或者重新配置磁盘控制器来重新获得。如果写障碍被关闭,请确认电池是否保持有效,一个有问题的电池可能会导致数据丢失。但愿文件系统和磁盘控制器设计师们将最终解决这种次优行为。
在操作系统向存储硬件发出一个写请求的时候,它没有什么好办法来保证数据真正到达非易失的存储区域。 实际上,确保所有存储部件都保证数据和文件系统元数据的完整性是管理员的责任。 避免使用那些没有电池作为后备的写高速缓存的磁盘控制器。在驱动器级别,如果驱动器不能保证在关闭(掉电)之前写入数据, 那么关闭回写高速缓冲。如果你在使用SSD,注意很多SSD默认都没有兑现高速缓存刷写命令。你可以使用diskchecker.pl来测试可靠的I/O子系统行为。
另外一个数据丢失的风险来自磁盘盘片写操作自身。磁盘盘片会被分割为扇区,通常每个扇区512字节。每次物理读写都对整个扇区进行操作。当一个写操作到达磁盘的时候,它可能是512 字节(PostgreSQL通常一次写8192字节或者16个扇区)的某个倍数,而写入处理可能因为电力失效在任何时候失败,这 意味着某些512字节的扇区写入了,而有些没有。为了避免这样的失效,PostgreSQL在修改磁盘上的实际页面之前, 周期地把整个页面的映像写入永久WAL存储。这么做之后,在崩溃恢复的时候,PostgreSQL可以从WAL恢复部分写入的页面。如果你的文件系统阻止部分页面写入(如ZFS),你可以通过关闭full_page_writes参数来关闭这种页映像。后备电池单元(BBU)磁盘控制器不阻止部分页面写入,除非它们保证数据都是以整页(8kB)写入到BBU。
PostgreSQL也能防止由于硬件错误或者介质失败超时在存储设备上造成的各种数据损坏,例如读/写垃圾数据。
-
WAL文件中的每一个记录都被一个CRC-32(32位)校验码所保护,这让我们可以判断记录内容是否正确。CRC值在我们写入每一个WAL记录时设置,并且在崩溃恢复、归档恢复和复制时检查。
-
目前数据页并没有默认地被校验,但是WAL记录中记录的整页映像将被保护。关于启用数据页校验的内容详见initdb。
-
诸如
pg_xact
、pg_subtrans
、pg_multixact
、pg_serial
、pg_notify
、pg_stat
、pg_snapshots
等内部数据结构既没有被直接校验,其页面也没有被整页写保护。但是,这些数据结构是持久的话,WAL记录被写入,它允许最近的修改能在崩溃恢复时被准确重建且这些WAL记录被按照以上讨论的方式保护着。 -
pg_twophase
中的单个状态文件被CRC-32保护。 -
用在大型SQL查询中排序的临时数据库文件、物化和中间结果目前没有被校验,对于这些文件的改变也不会导致写入WAL记录。
PostgreSQL无法避免可更正内存错误,它假定你会操作由工业标准纠错码(ECC)或更好方案保护的RAM。
二、预写式日志(WAL)
预写式日志(WAL)是保证数据完整性的一种标准方法。对其详尽的描述几乎可以在所有(如果不是全部)有关事务处理的书中找到。简单来说,WAL的中心概念是数据文件(存储着表和索引)的修改必须在这些动作被日志记录之后才被写入,即在描述这些改变的日志记录被刷到持久存储以后。如果我们遵循这种过程,我们不需要在每个事务提交时刷写数据页面到磁盘,因为我们知道在发生崩溃时可以使用日志来恢复数据库:任何还没有被应用到数据页面的改变可以根据其日志记录重做(这是前滚恢复,也被称为REDO)。
提示:
因为WAL在崩溃后恢复数据库文件内容,不需要日志化文件系统作为数据文件或WAL文件的可靠存储。实际上,日志会降低性能,特别是如果日志导致文件系统数据被刷写到磁盘。幸运地是,日志期间的数据刷写常常可以在文件系统挂载选项中被禁用,例如在Linux ext3文件系统中可以使用
data=writeback
。在崩溃后日志化文件系统确实可以提高启动速度。
使用WAL可以显著降低磁盘的写次数,因为只有日志文件需要被刷出到磁盘以保证事务被提交,而被事务改变的每一个数据文件则不必被刷出。日志文件被按照顺序写入,因此同步日志的代价要远低于刷写数据页面的代价。在处理很多影响数据存储不同部分的小事务的服务器上这一点尤其明显。此外,当服务器在处理很多小的并行事务时,日志文件的一个fsync
可以提交很多事务。
WAL也使得在线备份和时间点恢复能被支持。通过归档WAL数据,我们可以支持回转到被可用WAL数据覆盖的任何时间:我们简单地安装数据库的一个较早的物理备份,并且重放WAL日志一直到所期望的时间。另外,该物理备份不需要是数据库状态的一个一致的快照 — 如果它的制作经过了一段时间,则重放这一段时间的WAL日志将会修复任何内部不一致性。
三、异步提交
异步提交是一个允许事务能更快完成的选项,代价是在数据库崩溃时最近的事务会丢失。在很多应用中这是一个可接受的交换。
如前一节所述,事务提交通常是同步的:服务器等到事务的WAL记录被刷写到持久存储之后才向客户端返回成功指示。因此客户端可以确保那些报告已被提交的事务确会被保存,即便随后马上发生了一次服务器崩溃。但是,对于短事务来说这种延迟是其总执行时间的主要部分。选择异步提交模式意味着服务器将在事务被逻辑上提交后立刻返回成功,而此时由它生成的WAL记录还没有被真正地写到磁盘上。这将为小型事务的生产力产生显著地提升。
异步提交会带来数据丢失的风险。在向客户端报告事务完成到事务真正被提交(即能保证服务器崩溃时它也不会被丢失)之间有一个短的时间窗口。因此如果客户端将会做一些要求其事务被记住的外部动作,就不应该用异步提交。例如,一个银行肯定不会使用异步提交事务来记录一台ATM的现金分发。但是在很多情境中不需要这种强的保证,例如事件日志。
使用异步提交带来的风险是数据丢失,而不是数据损坏。如果数据库可能崩溃,它会通过重放WAL到被刷写的最后一个记录来进行恢复。数据库将因此被恢复到一个自身一致状态,但是任何还没有被刷写到磁盘的事务将不会反映在该状态中。因此其影响就是丢失了最后的少量事务。由于事务按照提交顺序被重放,所以不会出现任何不一致性 — 例如一个事务B按照前面一个事务A的效果来进行修改,则不会出现A的效果丢失而B的效果被保留的情况。
用户可以选择每一个事务的提交模式,这样可以有同步提交和异步提交的事务并行运行。这允许我们灵活地在性能和事务持久性之间进行权衡。提交模式由用户可设置的参数synchronous_commit控制,它可以使用任何一种修改配置参数的方法进行设置。一个事务真正使用的提交模式取决于当事务提交开始时synchronous_commit
的值。
特定的实用命令,如DROP TABLE
,被强制按照同步提交而不考虑synchronous_commit
的设定。这是为了确保服务器文件系统和数据库逻辑状态之间的一致性。支持两阶段提交的命令页总是同步提交的,如PREPARE TRANSACTION
。
如果数据库在异步提交和事务WAL记录写入之间的风险窗口期间崩溃,在该事务期间所作的修改将丢失。风险窗口的持续时间是有限制的,因为一个后台进程(“WAL写进程”)每wal_writer_delay毫秒会把未写入的WAL记录刷写到磁盘。风险窗口实际的最大持续时间是wal_writer_delay
的3倍,因为WAL写进程被设计成倾向于在忙时一次写入所有页面。
小心:
一个立刻关闭等同于一次服务器崩溃,因此也将会导致未刷写的异步提交丢失。
异步提交提供的行为与配置fsync = off不同。fsync
是一个服务器范围的设置,它将会影响所有事务的行为。它禁用了PostgreSQL中所有尝试同步写入到数据库不同部分的逻辑,并且因此一次系统崩溃(即,一个硬件或操作系统崩溃,不是PostgreSQL本身的失败)可能造成数据库状态的任意损坏。在很多情境中,带来大部分性能提升的异步提交可以通过关闭fsync
来获得,而且不会带来数据损坏的风险。
commit_delay也看起来很像异步提交,但它实际上是一种同步提交方法(事实上,commit_delay
在异步提交时被忽略)。commit_delay
会使事务在刷写WAL到磁盘之前有一个延迟,它期望由一个这样的事务所执行的刷写能够也服务于其他同时提交的事务。该设置可以被看成是一种时间窗口,在其期间事务可以参与到一次单一的刷写中,这种方式用于在多个事务之间摊销刷写的开销。
四、WAL配置
有几个WAL相关的配置参数会影响数据库性能。本节将解释它们的使用。
检查点是在事务序列中的点,这种点保证被更新的堆和索引数据文件的所有信息在该检查点之前已被写入。在检查点时刻,所有脏数据页被刷写到磁盘,并且一个特殊的检查点记录将被写入到日志文件(修改记录之前已经被刷写到WAL文件)。在崩溃时,崩溃恢复过程检查最新的检查点记录用来决定从日志中的哪一点(称为重做记录)开始REDO操作。在这一点之前对数据文件所做的任何修改都已经被保证位于磁盘之上。因此,完成一个检查点后位于包含重做记录的日志段之前的日志段就不再需要了,可以将其回收或删除(当WAL归档工作时,日志段在被回收或删除之前必须被归档)。
检查点对于刷写所有脏数据页到磁盘的要求可能会导致可观的I/O负载。出于这一原因,检查点活动是被有所限制的,这样I/O在检查点开始时开始并且能在下一个检查点将要开始之间完成,这使得检查点期间的性能下降被最小化。
服务器的检查点进程常常自动地执行一个检查点。检查点在每checkpoint_timeout秒开始,或者在快要超过 max_wal_size时开始。 默认的设置分别是 5 分钟和 1 GB。如果从前一个检查点以来没有WAL被写入, 则即使过了checkpoint_timeout
新的检查点也会被跳过( 如果正在使用WAL归档并且你想对文件被归档频率设置一个较低的限制来约束 潜在的数据丢失,你应该调整archive_timeout 参数而不是检查点参数)。也可以使用SQL命令 CHECKPOINT
来强制一个检查点。
降低checkpoint_timeout
和/或max_wal_size
会导致检查点更频繁地发生。这使得崩溃后恢复更快,因为需要重做的工作更少。但是,我们必须在这一点和增多的刷写脏数据页开销之间做出平衡。如果full_page_writes(默认情况),则还有一个因素需要考虑。为了确保数据页一致性,在每个检查点之后对一个数据页的第一次修改将导致整个页面内容被日志记录。在这情况下,一个较小的检查点间隔会增加输出到WAL日志的容量,这让使用较小间隔的效果打了折扣并且将导致更多的磁盘I/O。
检查点的代价相对比较昂贵,首先是因为它们要求写出所有当前为脏的缓冲区,正如以上讨论的,第二个原因是它们会导致额外的WAL流量。因此比较明智的做法是将检查点参数设置得足够高,这样检查点就不会过于频繁地发生。你可以设置checkpoint_warning参数作为对于你的检查点参数的一种简单完整性检查。如果检查点的发生时间间隔比checkpoint_warning
秒还要接近,一个消息将会被发送到服务器日志来推荐你增加max_wal_size
。偶尔出现的这样的消息并不会导致警报,但是如果它出现得太频繁,那么就应该增加检查点控制参数。 如果你没有把max_wal_size
设置得足够高, 那么在进行如大型COPY
传输等批量操作的时候可能会导致出现大量类似的警告消息。
为了避免大批页面写入对I/O系统产生的冲击,一个检查点中对脏缓冲区的写出操作被散布到一段时间上。这个时间段由checkpoint_completion_target控制,它用检查点间隔的一个分数表示。I/O率将被调整,以便能按照要求完成检查点:当checkpoint_timeout
给定的秒数已经过去,或者max_wal_size
被超过之前会发生检查点,以先达到的为准。默认值为0.5,PostgreSQL被期望能够在下一个检查点启动之前的大约一半时间内完成每个检查点。在一个接近于正常操作期间最大I/O的系统上,你可能希望增加checkpoint_completion_target
来降低检查点的I/O负载。但这种做法的缺点是被延长的检查点将会影响恢复时间,因为需要保留更多WAL段来用于可能的恢复操作。尽管checkpoint_completion_target
可以被设置为高于1.0,但最好还是让它小于1.0(也许最多0.9),因为检查点还包含除了写出脏缓冲区之外的其他一些动作。1.0的设置极有可能导致检查点不能按时被完成,这可能由于所需的WAL段数量意外变化导致性能损失。
在 Linux 和 POSIX 平台上,checkpoint_flush_after允许强制 OS 超过一个可配置的字节数后将检查点写入的页面刷入磁盘。否则,这些页面可能会被保留在 OS 的页面缓存中,当检查点结束发出fsync
时就会导致大量刷写形成延迟。这个设置通常有助于减小事务延迟,但是它也可能对性能带来负面影响,尤其是对于超过shared_buffers但小于 OS 页面缓存的负载来说更是如此。
pg_wal
目录中的 WAL 段文件数量取决于min_wal_size
、max_wal_size
以及在之前的检查点周期中产生的 WAL 数量。当旧的日志段文件不再被需要时,它们将被移除或者被再利用(也就是被重命名变成数列中未来的段)。如果由于日志输出率的短期峰值导致超过max_wal_size
,不需要的段文件将被移除直到系统回到这个限制以下。低于该限制时,系统会再利用足够的 WAL 文件来覆盖直到下一个检查点之前的需要。这种需要是基于之前的检查点周期中使用的 WAL 文件数量的移动平均数估算出来的。如果实际用量超过估计值,移动平均数会立即增加,因此它能在一定程度上适应峰值用量而不是平均用量。min_wal_size
对回收给未来使用的 WAL 文件的量设置了一个最小值,这个参数指定数量的 WAL 将总是被回收给未来使用,即便系统很闲并且 WAL 用量估计建议只需要一点点 WAL 时也是如此。
独立于max_wal_size
之外,wal_keep_segments + 1 个最近的 WAL 文件将总是被保留。还有,如果使用了 WAL 归档,旧的段在被归档之前不能被移除或者再利用。如果 WAL 归档无法跟上产生 WAL 的步伐,或者如果archive_command
重复失败,旧的 WAL 文件将累积在pg_wal
中,直到该情况被解决。一个使用了复制槽的较慢或者失败的后备服务器也会带来同样的效果。
在归档恢复模式或后备模式,服务器周期性地执行重启点。和正常操作时的检查点相似:服务器强制它所有的状态到磁盘,更新pg_control
来指示已被处理的WAL数据不需要被再次扫描,并且接着回收pg_wal
中的任何旧日志段文件。重启点的执行频率不能高于主机中检查点的执行频率,因为重启点只有在检查点记录处才能被执行。 如果从最后一个重启点之后过去了至少checkpoint_timeout
秒或者 WAL 尺寸快要达到max_wal_size
,则会到达一个检查点,这时会触发一个重启点。不过,因为对于何时可以执行一个重启点有限制,在恢复期间max_wal_size
常常被超过,最多会超过一个检查点周期间的 WAL(不管怎样,max_wal_size
从来不是一个硬限制,因此你应该总是应该留出充足的净空来避免耗尽磁盘空间)。
有两个常用的内部WAL函数:XLogInsertRecord
和XLogFlush
。 XLogInsertRecord
用于向共享内存中的WAL缓冲区里放置一个新记录。如果没有空间存放新记录, 那么XLogInsertRecord
就不得不写出(向内核缓存里写)一些填满了的WAL缓冲区。 这并非我们所期望的,因为XLogInsertRecord
用于每次数据库低层修改(比如,记录插入)时都要在受影响的数据页上持有一个排它锁,因为该操作需要越快越好。但糟糕的是, 写WAL缓冲可能还会强制创建新的日志段,这花的时间甚至更多。通常,WAL缓冲区应该由一个XLogFlush
请求来写和刷出, 在大部分时候它都是发生在事务提交的时候以确保事务记录被刷写到永久存储。在那些日志输出量比较大的系统上,XLogFlush
请求可能不够频繁,这样就不能避免XLogInsert
进行写操作。在这样的系统上,我们应该通过修改配置参数 wal_buffers的值来增加WAL缓冲区的数量。如果设置了 full_page_writes并且系统相当繁忙, 把wal_buffers
设置得更高一些将有助于在紧随每个检查点之后的时间段里得到平滑的响应时间。
commit_delay定义了一个组提交领导者进程在XLogFlush
中要求一个锁之后将会休眠的微秒数,而组提交追随者都排队等候在领导者之后。这样的延迟可以允许其它服务器进程把它们提交的记录追加到WAL缓冲区中,这样所有的这些记录将会被领导者的最终同步操作刷出。如果fsync被禁用或者当前处于活跃事务中的会话数少于commit_siblings,休眠将不会发生;这样就避免了在其它事务不会很快提交的情况下进行休眠。 请注意在某些平台上,休眠要求的单位是十毫秒,所以任何介于 1 和 10000 微秒之间的非零commit_delay
设置的作用都是一样的。 还要注意在某些平台上,休眠操作用的时间会比该参数所请求的要略长一点。
由于commit_delay
的目的是允许每次刷写操作的开销能够在并发提交的事务之间进行分摊(可能会以事务延迟为代价),在能够明智地选择该设置之前有必要对代价进行量化。代价越高,在一定程度上commit_delay
对于提高事务吞吐量的效果就越好。pg_test_fsync程序可以被用来衡量一次WAL刷写操作需要的平均微秒数。该程序报告的一次8kB写操作后的刷出所用的平均时间的一半常常是commit_delay
最有效的设置,因此在优化一种特定工作负荷时,该值被推荐为起始点。当WAL日志被存储在高延迟的旋转磁盘上时,调节commit_delay
特别有效,即使在具有非常快同步时间的存储介质上也能得到很显著的收益,例如固态驱动器或具有电池后备写高速缓存的RAID阵列。但是这应该在一个具有代表性的工作负荷下进行明确地测试。较高的commit_siblings
值应该用在这种情况中,反之较小的commit_siblings
值通常对高延迟介质有用。注意过高的commit_delay
设置也很有可能增加事务延迟甚至于整个事务吞吐量都会受到影响。
当commit_delay
被设置为0(默认值),仍然有可能出现组提交的形式,但是组中的成员只能是那些在前一个刷写操作发生过程窗口中需要刷写它们提交记录的会话。在较高的客户端数量时很可能发生“gangway effect”,因此即使commit_delay
为0,组提交的效果也很显著,并且显式地设置commit_delay
将会没有作用。设置commit_delay
只有在两种情况下有帮助:(1)有一些并发提交的事务,以及(2)吞吐量在某种程度上被提交率限制。但是在高旋转延迟的设备上,即使少到只有两个客户端,该设置也能有效提高事务吞吐量。
wal_sync_method参数决定PostgreSQL如何请求内核强制将WAL更新到磁盘。只要满足可靠性,那么除了fsync_writethrough
所有选项应该都是一样的,fsync_writethrough
可以在某些时候强制磁盘高速缓存的刷写,而其他选项不能这样做。不过,哪种选项最快则可能和平台密切相关。 你可以使用pg_test_fsync程序来测试不同选项的速度。请注意如果你关闭了fsync
,那么这个参数就无所谓了。
启用wal_debug配置参数(前提是PostgreSQL编译的时候打开了这个支持) 将导致每次XLogInsertRecord
和XLogFlush
WAL调用都被记录到服务器日志。这个选项以后可能会被更通用的机制取代。
五、WAL内部
WAL是自动被启用的。除了确保满足WAL日志存放所需要的磁盘空间以及一些必要的调优外(参阅第四节),管理员无需执行任何操作。
当每个新记录被写入时,WAL记录被追加到WAL日志中。 插入位置由日志序列号(LSN)描述,该日志序列号是日志中的字节偏移量, 随每个新记录单调递增。LSN值作为数据类型 pg_lsn返回。 值可以进行比较以计算分离它们的WAL数据量,因此它们用于衡量复制和恢复的进度。
WAL日志被存放在数据目录的pg_wal
目录里,它是作为一个文件段的集合存储的,通常每个段16MB大小(不过这个大小可以通过initdb配置选项--with-wal-segsize
来修改)。每个段分割成多个页,通常每个页为8K(该尺寸可以通过--with-wal-blocksize
配置选项来修改)。日志记录头部在access/xlogrecord.h
里描述;日志内容取决于它记录的事件类型。段文件的名字是不断增长的数字,从000000010000000000000000
开始。目前这些数字不能回卷,不过要把所有可用的数字都用光也需要非常非常长的时间。
日志被放置在和主数据库文件不同的另外一个磁盘上会比较好。你可以通过把pg_wal
目录移动到另外一个位置(当然在此期间服务器应当被关闭),然后在原来的位置上创建一个指向新位置的符号链接来实现重定位日志。
WAL的目的是确保在数据库记录被修改之前先写了日志,但是这可能会被那些谎称向内核写成功的破坏, 这时候它们实际上只是缓冲了数据而并未把数据存储到磁盘上。 这种情况下的电源失效仍然可能导致不可恢复的数据崩溃。 管理员应该确保保存PostgreSQL的WAL日志文件的磁盘不会做这种谎报(参见第一节)。
在完成一个检查点并且刷写了日志文件之后,检查点的位置被保存在文件pg_control
里。因此在恢复的开始, 服务器首先读取pg_control
,然后读取检查点记录; 接着它通过从检查点记录里标识的日志位置开始向前扫描执行 REDO操作。 因为数据页的所有内容都保存在检查点之后的第一个页面修改的日志里(假设full_page_writes没有被禁用), 所以自检查点以来的所有变化的页都将被恢复到一个一致的状态。
为了处理pg_control
被损坏的情况, 我们应该支持对于现有日志段反向扫描的功能 — 从最新到最老 — 这样才能找到最后的检查点。但这些目前还没有被实现。pg_control
很小(比一个磁盘页小),因此它不会出现页断裂问题, 并且到目前为止还没有发现仅仅由于无法读取pg_control
本身导致数据库失败的报告。 因此,尽管这在理论上是一个薄弱环节,但是pg_control
看起来似乎并不是实际会发生的问题。
这篇关于PostgreSQL教程(三十三):服务器管理(十五)之可靠性和预写式日志的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!