30.1. Reliability

可靠性是任何严肃的数据库系统的重要属性,而 PostgreSQL 尽一切可能保证可靠的操作。可靠操作的一个方面是,已提交事务记录的所有数据都应存储在非易失性区域中,这样可以防止断电,os 故障和硬件故障(当然,非易失性区域本身的故障除外)。通常,成功将数据写入计算机的永久存储(磁盘驱动器或等效驱动器)即可满足此要求。实际上,即使一台计算机受到了致命的损坏,如果磁盘驱动器幸免于难,也可以将它们移动到具有类似硬件的另一台计算机上,并且所有提交的事务将保持不变。

虽然周期性地将数据强制到磁盘上看起来很简单,但事实并非如此。由于磁盘驱动器比主内存和 CPU 慢得多,因此在计算机的主内存和磁盘片之间存在几层缓存。首先,是 os 的缓冲区高速缓存,该缓冲区高速缓存经常请求的磁盘块并合并磁盘写入。幸运的是,所有 os 都为应用程序提供了一种强制从缓冲区高速缓存写入磁盘的方法,而 PostgreSQL 使用了这些功能。 (请参阅wal_sync_method参数来调整操作方式。)

接下来,磁盘驱动器控制器中可能存在高速缓存;这在 RAID 控制器卡上尤其常见。这些缓存中的一些是* write-through (直写)的,这意味着写入一旦到达就会发送到驱动器。其他的是 write-back ,这意味着数据稍后会发送到驱动器。由于磁盘控制器高速缓存中的内存易失,并且在电源故障时会丢失其内容,因此此类高速缓存可能会造成可靠性危害。更好的控制器卡具有Batteries 备份单元(BBU),这意味着该卡具有一个 Batteries,可以在系统断电的情况下为高速缓存供电。恢复电源后,数据将被写入磁盘驱动器。

最后,大多数磁盘驱动器都有缓存。有些是直写的,有些则是回写的,对于写回驱动器缓存和磁盘控制器缓存,也存在着有关数据丢失的担忧。消费级的 IDE 和 SATA 驱动器特别可能具有回写缓存,这些缓存在电源故障后将无法幸免。许多固态驱动器(SSD)也具有易失性回写缓存。

通常可以禁用这些缓存。但是,执行此操作的方法因 os 和驱动器类型而异:

  • 在 Linux 上,可以使用hdparm -I查询 IDE 和 SATA 驱动器;如果Write cache旁边有*,则启用写缓存。 hdparm -W 0可用于关闭写缓存。可以使用sdparm查询 SCSI 驱动器。使用sdparm --get=WCE检查是否启用了写缓存,并使用sdparm --clear=WCE禁用它。

  • 在 FreeBSD 上,可以使用atacontrol来查询 IDE 驱动器,并可以使用/boot/loader.conf中的hw.ata.wc=0来关闭写缓存。可以使用camcontrol identify来查询 SCSI 驱动器,并且可以使用sdparm来查询和更 Rewrite 高速缓存。

  • 在 Solaris 上,磁盘写缓存由format -e控制。 (Solaris ZFS 文件系统在启用磁盘写缓存的情况下是安全的,因为它会发出自己的磁盘缓存刷新命令.)

  • 在 Windows 上,如果wal_sync_methodopen_datasync(默认设置),则可以通过取消选中My Computer\Open\disk drive\Properties\Hardware\Properties\Policies\Enable write caching on the disk来禁用写缓存。或者,将wal_sync_method设置为fsyncfsync_writethrough,这样可以防止写缓存。

  • 在 macOS 上,可以通过将wal_sync_method设置为fsync_writethrough来防止写缓存。

最新的 SATA 驱动器(ATAPI-6 或更高版本之后的驱动器)提供驱动器缓存刷新命令(FLUSH CACHE EXT),而 SCSI 驱动器早就支持类似的命令SYNCHRONIZE CACHE。 PostgreSQL 不能直接访问这些命令,但是某些文件系统(例如 ZFS,ext4)可以使用它们将数据刷新到启用回写驱动器的磁盘上。不幸的是,当与 Batteries 备份单元(BBU)磁盘控制器结合使用时,此类文件系统的运行情况并非最佳。在这种设置中,synchronize 命令将所有数据从控制器高速缓存强制到磁盘,从而消除了 BBU 的许多好处。您可以运行pg_test_fsync程序以查看是否受到影响。如果您受到影响,则可以通过关闭文件系统中的写屏障或重新配置磁盘控制器(如果可以)来重新获得 BBU 的性能优势。如果写屏障已关闭,请确保 Batteries 仍可工作;Batteries 故障可能会导致数据丢失。希望文件系统和磁盘控制器设计者最终将解决这种次优的行为。

当 os 将写请求发送到存储硬件时,几乎没有办法确保数据已经到达 true 的非易失性存储区域。而是由 Management 员负责确保所有存储组件都确保数据和文件系统元数据的完整性。避免使用没有 Batteries 支持的写缓存的磁盘控制器。在驱动器级别,如果驱动器不能保证在关机之前将数据写入,请禁用回写缓存。如果使用 SSD,请注意,其中许多默认情况下都不遵循高速缓存刷新命令。您可以使用diskchecker.pl测试可靠的 I/O 子系统行为。

数据磁盘拼写操作本身也会带来数据丢失的另一风险。磁盘盘分为多个扇区,每个扇区通常为 512 字节。每个物理读或写操作都处理整个扇区。当写入请求到达驱动器时,它可能是 512 字节的某个倍数(PostgreSQL 通常一次写入 8192 字节或 16 个扇区),并且写入过程可能随时因断电而失败,这意味着某些 512 字节的扇区已写入,而其他则未写入。为了防止此类故障,PostgreSQL 会在修改磁盘上的实际页面之前,定期将整页图像写入永久性 WAL 存储。这样,在崩溃恢复期间,PostgreSQL 可以从 WAL 还原部分编写的页面。如果您具有防止部分页面写入的文件系统软件(例如 ZFS),则可以通过关闭full_page_writes参数来关闭此页面映像。Batteries 后备单元(BBU)磁盘控制器不会阻止部分页面写入,除非它们保证数据以完整(8kB)页面写入到 BBU。

PostgreSQL 还可以防止存储设备上的某些数据损坏,这些损坏可能是由于硬件错误或介质随时间推移而发生故障,例如读取/写入垃圾数据。

  • WAL 文件中的每个单独记录都受到 CRC-32(32 位)检查的保护,该检查使我们可以判断记录内容是否正确。当我们写入每个 WAL 记录时设置 CRC 值,并在崩溃恢复,归档恢复和复制期间进行检查。

  • 默认情况下,默认情况下不对数据页进行校验和,尽管将保护 WALLogging 记录的全页图像;有关启用数据页校验和的详细信息,请参见initdb

  • 内部数据结构(例如pg_xactpg_subtranspg_multixactpg_serialpg_notifypg_statpg_snapshots)不会直接进行校验和,也不会受到整页写操作的保护。但是,在此类数据结构持久的情况下,将写入 WAL 记录,以允许在崩溃恢复时准确地重建最近的更改,并且如上所述保护了这些 WAL 记录。

  • pg_twophase中的各个状态文件受 CRC-32 保护。

  • 大型 SQL 查询中用于排序,实现和中间结果的临时数据文件目前尚未校验和,也不会为这些文件的更改而写入 WAL 记录。

PostgreSQL 不能防止可纠正的内存错误,并且假定您将使用使用行业标准的纠错码(ECC)或更好保护的 RAM 进行操作。