2024年9月26日:PostgreSQL 17 发布!
支持的版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:开发版
不支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2

28.1. 可靠性 #

可靠性是任何严肃的数据库系统的重要属性,而 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 上,可以使用 camcontrol identify 查询 IDE 驱动器,并使用 /boot/loader.conf 中的 hw.ata.wc=0 关闭写入缓存;可以使用 camcontrol identify 查询 SCSI 驱动器,并在可用时使用 sdparm 查询和更改写入缓存。

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

  • Windows 上,如果 wal_sync_methodopen_datasync(默认值),则可以通过取消选中 我的电脑\打开\磁盘驱动器\属性\硬件\属性\策略\启用磁盘上的写入缓存 来禁用写入缓存。或者,将 wal_sync_method 设置为 fdatasync(仅限 NTFS)或 fsync,这可以防止写入缓存。

  • 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-32C(32 位)校验保护,这使我们能够判断记录内容是否正确。CRC 值在写入每个 WAL 记录时设置,并在崩溃恢复、归档恢复和复制期间检查。

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

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

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

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

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

提交更正

如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符,或者需要进一步澄清,请使用 此表单 报告文档问题。