29.1. 可靠性

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

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

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

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

这些缓存通常可以禁用,然而,禁用的方法会因操作系统和驱动类型而不同。

最近的SATA驱动器(在ATAPI-6之后或更晚一些的)提供一个驱动器缓存刷写命令(FLUSH CACHE EXT), 而SCSI驱动器长期以来支持一个类似的命令SYNCHRONIZE CACHE。这些命令不能直接访问PostgreSQL, 但是某些文件系统(例如,ZFSext4)可以使用它们来刷写数据到启用了回写的驱动器上的盘片上。 不幸的是,这样的文件系统与电池备份单元(BBU)磁盘驱动器结合使用时效果并不理想。在这样的设置中, 同步命令强制所有数据从驱动器缓存到磁盘,消除了BBU的好处。你可以运行pg_test_fsync 程序来查看你是否受影响。如果你受到影响,BBU的性能优势可以通过在文件系统中关掉写屏障或重新配置磁盘控制器重新获得,前提是这是一个选项。如果关闭了写屏障,确保电池仍然运行;失效的电池可能导致数据丢失。 希望文件系统和磁盘控制器设计者最终解决这个并不理想的行为。

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

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

PostgreSQL也能阻止某些因为硬件错误或介质失效可能发生在存储驱动器上的数据损坏, 比如读/写垃圾数据。

PostgreSQL不预防矫正内存错误,它假设您将使用具有工业标准纠错编码(ECC) 的内存操作或更好的保护。