28.2. 数据校验和 #
默认情况下,数据页受校验和保护,但也可以选择为集簇禁用。启用后,每个数据页都包含一个校验和,在页面写入时更新,并在页面每次被读取时进行验证。只有数据页受校验和保护;内部数据结构和临时文件不受保护。
可以在使用 initdb 初始化集簇时禁用校验和。之后也可以作为离线操作,或者在允许并发访问的运行中集簇上在线启用或禁用。数据校验和是在整个集簇级别启用或禁用的,不能为单个数据库、表或复制的集簇成员分别指定。
可以通过查看只读配置变量 data_checksums 的值来确认集簇当前的校验和状态,执行命令 SHOW data_checksums 即可。还可以使用 pg_control_init 和 pg_control_checkpoint,分别检查集簇初始化时和当前检查点的数据校验和状态。由于最初实现方式的缘故,数据校验和状态通常也称为数据校验和版本,以整数表示。表 28.1 列出了状态名称与版本之间的映射,它们定义在 src/include/storage/checksum.h 中。
表 28.1. 数据校验和状态与版本映射
| 状态 | 版本 |
|---|---|
| off | 0 |
| on | 1 |
| inprogress-off | 2 |
| inprogress-on | 3 |
在尝试从页面损坏中恢复时,可能需要绕过校验和保护。为此,请临时设置配置参数 ignore_checksum_failure。
28.2.1. 离线启用校验和 #
pg_checksums 程序可用于在离线集簇上启用或禁用数据校验和,也可以用来验证校验和。
28.2.2. 在线启用校验和 #
可以通过调用相应的函数在线启用或禁用校验和。
启用和禁用数据校验和都分两个阶段完成,中间由一次检查点分隔,以确保持久性。不同状态及其转换关系如图 28.1 所示,本节将进一步说明。
图 28.1. 数据校验和状态
启用校验和会把集簇校验和状态设为 inprogress-on。在此期间,校验和会被写入,但不会被验证。除此之外,还会启动一个后台工作进程,用于为集簇中所有现有数据启用校验和。该工作进程完成集簇中所有数据库的处理后,校验和状态会自动切换为 on。该处理会占用两个后台工作进程,因此要确保 max_worker_processes 至少还能容纳另外两个进程。
该过程在开始前会先等待所有打开的事务结束,以便确认不存在在尚未提交的事务内部创建、从而对启用校验和的过程不可见的表。它还会在完成前,针对每个数据库等待所有预先存在的临时表被移除。如果应用中使用了生命周期较长的临时表,则可能需要终止这些应用连接,以便该过程完成。
如果集簇在 inprogress-on 状态下由于任何原因停止,或者处理被中断,那么必须手工重新启动校验和启用过程。为此,在集簇重启后重新执行
pg_enable_data_checksums() 函数。该过程会从头开始,不支持从中断处继续。若集簇在 inprogress-off 状态下停止,则在集簇重启时,校验和状态会被设为 off。
禁用数据校验和会把数据校验和状态设为 inprogress-off。在此期间,校验和会被写入,但不会被验证。所有进程确认该变更后,状态会自动设为
off。
在数据校验和正在被启用时禁用数据校验和,会中止当前处理。
28.2.2.1. 在线操作对系统的影响 #
启用数据校验和会给系统带来显著的 I/O 压力,因为所有数据库页面都需要重写,并且既要写入数据文件,也要写入 WAL。可以通过
pg_enable_data_checksums() 函数的
cost_delay 和 cost_limit
参数进行节流,从而限制影响。
I/O:所有页面都需要计算并写入数据校验和,这会生成大量脏页,之后这些脏页还需要刷盘,同时也会写入 WAL。
复制:当备库在 WAL 流中收到数据校验和状态变更时,它会发起一个重启点,以便把当前状态刷入
pg_control文件。重启点会把当前状态刷到磁盘,并在完成前阻塞重做。这反过来会引入复制延迟,在同步备库上还会阻塞主库。在过程启动前减小 max_wal_size,有助于缩短重启点完成所需的时间。关闭/重启:如果在启用数据校验和期间服务器被关闭或重启,处理过程不会恢复,所有页面都需要重新计算并重写。应当在无需常规维护或处于服务窗口期间启用数据校验和。
禁用数据校验和时不会产生 I/O,但仍然需要检查点。
28.2.3. 复制集簇中不匹配的数据校验和状态 #
由于离线和在线校验和操作具有不同的持久性模型,主库和备库最终可能处于不同的数据校验和状态。
在线转换会记录到 WAL 中,这意味着它会在崩溃后被重放,并且该转换及所有校验和更新都会传播到备库。离线更改只记录在集簇的控制文件中,它不写入 WAL,对复制不可见。为写入校验和而对数据文件所做的更改不会记录到 WAL。这意味着它相对于节点尚未重放的 WAL 没有确定的顺序。当节点随后重放包含在线校验和状态更改的 WAL 时,即使该更改的写入先于离线更改,它也会在节点上生效。
离线更改只影响其运行所在的数据目录;新状态不会通过复制传播。这意味着可以并行处理所有节点,并且不会产生额外的网络流量或 WAL 归档流量。在复制环境中,必须在所有节点都停止时对所有节点应用相同的更改,如 pg_checksums 所述。状态发生分叉的备库会记录一条警告,但保留其本地设置。这种不匹配会持续存在,直到通过离线流程或在线转换让各状态重新一致。
不支持也不建议在复制集簇中的不同节点上以不同的数据校验和状态运行。