可靠性是任何严肃的数据库系统的重要属性,并且 PostgreSQL 尽一切可能确保其可靠运行。可靠运行的一个方面是,已提交事务记录的所有数据都应存储在非易失性区域,该区域可以免受断电、操作系统故障和硬件故障(当然,除了非易失性区域本身发生故障)的影响。成功将数据写入计算机的永久存储(磁盘驱动器或同等设备)通常能满足此要求。事实上,即使计算机遭到毁灭性损坏,如果磁盘驱动器得以幸存,它们也可以被移动到另一台具有相似硬件的计算机上,并且所有已提交的事务都将保持完整。
虽然定期将数据强制写入磁盘可能看起来是一项简单的操作,但事实并非如此。由于磁盘驱动器的速度远低于主内存和 CPU,因此在计算机主内存和磁盘之间存在多个缓存层。首先,有操作系统本身的缓冲区缓存,它缓存频繁请求的磁盘块并合并磁盘写入。幸运的是,所有操作系统都为应用程序提供了一种将写入从缓冲区缓存强制写入磁盘的方法,并且 PostgreSQL 使用了这些功能。(有关如何执行此操作的调整,请参阅 wal_sync_method 参数。)
接下来,磁盘驱动器控制器可能有一个缓存;这在RAID控制器卡上尤其常见。其中一些缓存是写通 (write-through) 模式,意味着写入在到达时立即发送到驱动器。其他缓存是写回 (write-back) 模式,意味着数据将在稍后某个时间发送到驱动器。此类缓存可能存在可靠性风险,因为磁盘控制器缓存中的内存是易失性的,在断电时会丢失其内容。更好的控制器卡具有电池备份单元 (battery-backup units)(BBU) ,这意味着在系统断电时,该卡会有一个电池为缓存供电。恢复供电后,数据将写入磁盘驱动器。
最后,大多数磁盘驱动器都有缓存。有些是写通模式,有些是写回模式,并且对于写回驱动器缓存,存在与磁盘控制器缓存相同的数据丢失担忧。消费级 IDE 和 SATA 驱动器尤其可能拥有在断电时无法幸存的写回缓存。许多固态驱动器(SSD)也具有易失性的写回缓存。
这些缓存通常可以被禁用;然而,禁用方法因操作系统和驱动器类型而异。
在 Linux 上,可以使用 hdparm -I
查询 IDE 和 SATA 驱动器;如果 Write cache
旁边有一个 *
,则表示写缓存已启用。hdparm -W 0
可用于关闭写缓存。SCSI 驱动器可以使用 sdparm 进行查询。使用 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_method
设置为 open_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 记录中记录的完整页面映像始终受校验和保护。
内部数据结构,如 pg_xact
、pg_subtrans
、pg_multixact
、pg_serial
、pg_notify
、pg_stat
、pg_snapshots
,不直接进行校验和检查,页面也不受完整页面写入的保护。但是,对于这些持久化的数据结构,会写入 WAL 记录,以便在崩溃恢复时准确地重建最近的更改,并且这些 WAL 记录会如上所述受到保护。
pg_twophase
中的单个状态文件受 CRC-32C 保护。
在较大的 SQL 查询中用于排序、物化和中间结果的临时数据文件目前不进行校验和检查,也不会为这些文件的更改写入 WAL 记录。
PostgreSQL 不会防御可纠正的内存错误,并且假设您将使用带有行业标准错误纠正码(ECC)或更好保护的 RAM 进行操作。
如果您在文档中看到任何不正确、与您对特定功能的实际体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。