可靠性是任何正规数据库系统的特性,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
中使用 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) 磁盘控制器。在这样的设置中,synchronize 命令将控制器缓存中所有数据强制至磁盘,因此 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 位)校验的保护,这允许我们判断记录内容是否正确。在写入每个 WAL 记录时设置 CRC 值,并在崩溃恢复、存档恢复和复制期间检查 CRC 值。
数据页面当前默认不执行校验和,但会保护在 WAL 记录中记录的完整页面映像;有关启用数据校验和的详细信息,请参见 initdb。
不会直接对 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 来进行操作。