Skip to content

30.1. 可靠性#

可靠性是任何严肃数据库系统的一个重要特性,PostgreSQL竭尽全力保证可靠运行。可靠运行的一个方面是已提交事务记录的所有数据都应存储在非易失性区域,该区域不受断电、操作系统故障和硬件故障的影响(当然,非易失性区域本身故障除外)。通常,将数据成功写入计算机的永久存储(磁盘驱动器或同等设备)会满足此要求。事实上,即使计算机遭到致命损坏,如果磁盘驱动器幸存下来,它们也可以移动到具有类似硬件的另一台计算机,并且所有已提交的事务都将保持完整。

虽然定期强制将数据写入磁盘盘片似乎是一个简单的操作,但事实并非如此。由于磁盘驱动器比主内存和 CPU 慢得多,因此计算机主内存和磁盘盘片之间存在多层缓存。首先,有操作系统的缓冲区缓存,它缓存频繁请求的磁盘块并合并磁盘写入。幸运的是,所有操作系统都为应用程序提供了一种方法,可强制将缓冲区缓存中的写入内容写入磁盘,PostgreSQL使用这些功能。(请参阅wal_sync_method参数以调整执行此操作的方式。)

接下来,磁盘驱动器控制器中可能有一个缓存;这在RAID控制器卡上尤其常见。其中一些缓存是直写,这意味着写入在到达后立即发送到驱动器。其他一些是回写,这意味着数据在稍后的某个时间发送到驱动器。此类缓存可能是一个可靠性隐患,因为磁盘控制器缓存中的内存是易失性的,并且在断电时会丢失其内容。更好的控制器卡具有电池备份单元(BBUs),这意味着该卡具有一个电池,在系统断电时可为缓存保持供电。恢复供电后,数据将写入磁盘驱动器。

最后,大多数磁盘驱动器都有缓存。一些是直写,而另一些是回写,并且回写驱动器缓存和磁盘控制器缓存都存在相同的数据丢失问题。消费级 IDE 和 SATA 驱动器尤其可能具有在断电时无法存活的回写缓存。许多固态驱动器 (SSD) 也有易失性回写缓存。

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

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

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

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

  • Windows上,如果wal_sync_methodopen_datasync(默认值),则可以通过取消选中我的电脑\打开\磁盘驱动器\属性\硬件\属性\策略\在磁盘上启用写缓存来禁用写缓存。或者,将wal_sync_method设置为fdatasync(仅限 NTFS)、fsyncfsync_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 位)校验的保护,这使我们能够判断记录内容是否正确。在写入每条 WAL 记录时设置 CRC 值,并在崩溃恢复、归档恢复和复制期间进行检查。

  • 默认情况下,目前不会对数据页进行校验和,但记录在 WAL 记录中的完整页映像将受到保护;有关启用数据校验和的详细信息,请参阅 initdb

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

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

  • 目前不会对用于较大的 SQL 查询中的排序、物化和中间结果的临时数据文件进行校验和,也不会为对这些文件的更改写入 WAL 记录。

PostgreSQL不会抵御可纠正的内存错误,并且假定您将使用采用行业标准错误纠正码 (ECC) 或更高级别的保护的 RAM 进行操作。