在 PostgreSQL 里,SHOW data_checksums 只会返回 on 或 off——和 block_size 一样,它是个只读的预设参数,初始化集群时就定了,之后无法通过 ALTER SYSTEM 修改。但这个看似平淡的开关,背后是一场持续了十三年、至今仍在演进的数据完整性战役。
页面校验和到底在查什么
PostgreSQL 的数据文件由一个个固定大小的页面(默认 8KB)组成。每次读写页面时,操作系统和硬件并不保证内容完好——磁盘静默损坏、内存位翻转、内核 bug 都可能让一个页面悄悄变质,而数据库直到下次真正解析该页面时才会发现异常,甚至可能永远发现不了。
data_checksums = on 后,每个页面写入磁盘前都会计算一个校验和并写入页面头部;读取时重新计算并比对。一旦不匹配,PostgreSQL 立刻报错,而不是让损坏数据静默传播。
查看当前集群状态,最直接的方式:
-- 在 psql 中执行
SHOW data_checksums;
返回 on 或 off。你也可以用底层工具确认:
pg_controldata /var/lib/postgresql/16/main | grep checksum
输出类似:
Data page checksum version: 1
版本号为 1 表示校验和已启用,0 表示关闭。
十三年的曲折历程
校验和的故事从 2013 年 PostgreSQL 9.3 开始——这是第一个支持 initdb --data-checksums 的版本。但当时的设计有一个硬限制:只能在初始化时开启,已有集群无法追加启用。大量生产库已经跑起来了,想加校验和只能逻辑导出再导入,代价极高。
社区花了多年时间解决这个问题。关键里程碑:
- 9.3:
initdb时可选开启,一旦开启不可关闭。 - 11:引入
pg_checksums命令行工具,可以在停机状态下对已有集群启用或禁用校验和,不再需要重初始化。 - 12+:逐步优化校验和算法的性能开销,社区持续讨论是否提供在线启用(不停机)的能力。
至今,在线启用校验和仍是未完全落地的功能——这正是 Christophe Pettus 所说"历史仍在书写"的部分。
用 pg_checksums 给现有集群启用校验和
如果你的集群是 11 或更高版本,且当前 data_checksums = off,可以在停机窗口内用 pg_checksums 一次性启用:
# 1. 先确认当前状态
pg_checksums -c /var/lib/postgresql/16/main
# 输出: Checksums are disabled in cluster "/var/lib/postgresql/16/main"
# 2. 停库
pg_ctl -D /var/lib/postgresql/16/main stop -m fast
# 3. 启用校验和(-e 表示 enable,-P 显示进度)
pg_checksums -e -P /var/lib/postgresql/16/main
# 输出进度条,扫描并写入所有文件的校验和
# 4. 确认结果
pg_checksums -c /var/lib/postgresql/16/main
# 输出: Checksums are enabled in cluster ...
# 5. 启库
pg_ctl -D /var/lib/postgresql/16/main start
启用后,pg_stat_database 视图新增了 checksum_failures 列,可以实时监控校验失败次数:
SELECT datname, checksum_failures
FROM pg_stat_database
WHERE checksum_failures > 0;
一旦出现非零值,说明已经有页面在磁盘上损坏了——这是你原本可能永远不会察觉的问题。
性能开销与取舍
校验和不是免费的。每次页面写入要计算并存储校验和,每次读取要验证。社区基准测试显示,典型 OLTP 场景的性能下降在 1%–5% 之间,具体取决于写入频率和页面大小。对于以读为主的工作负载,影响更小。
几个值得注意的细节:
- 全页写(full_page_writes)与校验和的关系:WAL 的全页写机制本身也会保护第一次修改后的页面,但校验和覆盖的是所有后续读取场景,两者互补而非替代。
- 大页面(huge pages):使用 Linux huge pages 时,校验和同样生效,但内存位翻转的风险本身在 huge pages 上略高(因为管理粒度更大),启用校验和反而更有意义。
- 复制场景:主库开启校验和后,备库在接收 WAL 并回放时也会验证页面校验和,损坏会在备库侧被检出。
什么时候该开启——一个决策清单
| 场景 | 建议 |
|---|---|
| 新建集群 | 直接 initdb --data-checksums,没有理由不开 |
| 现有生产库(PG ≥ 11) | 在下一个计划停机窗口用 pg_checksums -e 启用 |
| 现有生产库(PG < 11) | 升级到 11+ 后再启用,或通过逻辑迁移重建 |
| 对延迟极度敏感的 OLTP | 先在测试环境跑基准,确认 1%–5% 的开销可接受 |
| 只读分析库 | 开启几乎没有负面影响,强烈建议启用 |
核心判断逻辑很简单:静默数据损坏是不可逆的灾难,而校验和的性能开销是可测量的、可控的。除非你的系统对每毫秒都不可妥协,否则开启校验和是更安全的默认选择。
最后一点:如果你已经在运行中发现了 checksum_failures > 0,不要慌,但不要拖延——这意味着磁盘或文件系统已经在丢失数据。立刻排查存储硬件,并从备份恢复受影响的数据库,而不是继续在损坏的页面上读写。