29.1. 可靠性

可靠性是任何严肃的数据库系统的重要属性,PostgreSQL 尽一切可能来保证可靠的操作。可靠性操作的一个方面是所有已提交 的数据都应该存储在一个非易失的区域,这样就不会因为电力失效、 操作系统崩溃、硬件失效(除了非易失区域自身失效之外)等原因导致数据丢失。 向计算机的永久存储(磁盘驱动器或者等效的东西)成功写入数据通常可以满足这个要求。 实际上,即使计算机完全失效,只要磁盘驱动器生存下来,那么它们就可以移动到另外一 台类似硬件的计算机上,而所有已经提交的事务将保持原状。

周期性地强制数据写入磁盘盘片看上去像一件简单的操作, 但实际上不是。因为磁盘驱动器比内存和 CPU 要慢许多, 在计算机的主存和磁盘盘片之间存在多层缓冲。 首先, 有操作系统的缓冲区内存,它缓冲常用的磁盘块并且组合 对磁盘写入的请求。幸运的是,所有操作系统都给予应用一个 强制从缓冲区写入磁盘的方法,PostgreSQL 使用了该特性(参阅wal_sync_method 参数)。

然后,在磁盘驱动器的控制器上可能还有一个缓冲; 特别是在RAID控制卡上更为常见。 这些缓冲区中,有些是透过式写入,意思是写入动作 在到达的同时写入到磁盘上。其它则是回写式写入, 意思是数据将在稍后写入驱动器。这样的缓冲区是可靠性的危害, 因为磁盘控制器上的内存是易失的,在发生电力失效的情况下会丢失其中的内容。 好一些的控制器卡备有电池供电的缓冲区( BBU的 S),这意味着该卡具有的电池 可以在系统电力失效的情况下提供电力。在电力恢复之后,这些数据将会被写入磁盘驱动器。

最后,大多数磁盘驱动器自身也有缓冲区。有些是透过式的, 有些是回写式的,和磁盘控制器一样,回写式的磁盘缓冲区也存在数据丢失的问题。 消费级别的IDE和SATA驱动器特别容易包含回写式缓冲,在掉电的情况下很容易丢失数据。 大多固态硬盘(SSD)同样有回写缓冲区。

这些缓冲区通常是不可用的;但这种方法随着操作系统和驱动类型的改变:

最新的SATA驱动(在ATAPI-6或更高版本后)提供一个引导缓存刷新的命令(FLUSH CACHE EXT), 而SCSI驱动长期以来提供一个更简短的命令:SYNCHRONIZE CACHE。 这些命令不能直接作用在PostgreSQL上,但是一些文件系统(例如, ZFS, ext4) 通过回写功能的驱动能够使用命令在磁盘上对数据刷新。但是,当绑定电池备份单元磁盘控制器时, 文件系统表现的性能不理想。在这样的设置下,同步命令强制控制器缓存内的所有数据 写进磁盘中,会掩盖BBU的大部分的优点。你可以运行在PostgreSQL的资源树的功能src/tools/fsync 来查看是否受到影响。如果受到影响,并且在文件系统或磁盘控制器重新配置的关闭可选的话, BBU的性能优势能够恢复。如果写入阻碍关闭了,确保电池的长期平稳运行, ,否则一股损坏的电池会导致数据的丢失。

在操作系统向磁盘硬件发出一个写请求的时候,它没有什么好办法来保证数据 真正到达非易失的存储区域。实际上,确保所有存储部件都保证数据的完整性是管理员的责任。 应该避免使用没有电池供电的回写缓冲磁盘控制器。在磁盘级别, 如果驱动器不能保证在关闭(掉电)之前写入数据,那么应该关闭回写缓冲。 diskchecker.pl. 如果你使用的是固态硬盘要注意在默认情况下,很多情况是不会按照缓冲刷新命令执行的。 你可以利用diskchecker.pl对子系统的稳定I/O尝试下。

另外一个数据丢失的风险来自磁盘盘片写操作自身。磁盘盘片会被分割为段, 通常每段 512 字节。每次物理读写都对整个段进行操作。当一个写操作到达磁盘的时候, 它可能是512字节、1024字节、或者8192字节(在一段时间的PostgreSQL通常写入8192字节,或 16个部门),而写入操作可能因为电力失效而随时失败,意味着某些 512 字节的段写入了, 而另一些则没有。为了避免这个问题,PostgreSQL在修改磁盘上的实际 页面之前周期性地把整个页面的影像写入永久存储。这样,在崩溃恢复的时候, PostgreSQL就可以从WALL恢复部分写入的页面。 如果你有一个电池供电的磁盘控制器或者是文件系统(比如ZFS)自身能够避免部分页面写入, 你可以通过full_page_writes参数来关闭页面影像功能。 电池备份单元(BBU)磁盘控制器不会阻止部分页面写入,除非 他们保证,数据被写入全页(8KB)的BBU。