28.6 `amcheck` 与例行完整性检查
备份没有报错,不表示每个 B-tree 都满足搜索不变量;checksum 开启,也不表示 heap 与 index 逻辑一致。
完整性不是一个布尔值,而是多层故障面:
amcheck 覆盖其中重要的一段:heap 与若干 index access method 的结构/逻辑检查。它是
检测工具,不是修复器,更不是 backup 或 restore 的替代品。
28.6.1 bt_index_check 与更深检查的成本
bt_index_check:日常 B-tree 基线
它检查多种 B-tree 不变量。若发现逻辑不一致,通常抛错;无输出/无错意味着:
在本次检查覆盖的范围内没有发现问题。
不意味着:
这个索引、heap、存储设备和所有备份绝对无损坏。
锁:
与普通 SELECT 使用的 relation lock mode 相同。官方文档把它视为 live production
日常轻量检查的较好折中。
先筛合法对象
批量检查不要把 temp、invalid、非 B-tree 对象误传:
对 unique index,checkunique=true 检查重复 entry 中不应有多个可见版本;这是额外
工作,但更贴合 uniqueness 语义。
bt_index_parent_check:更强锁、更深结构
它是 bt_index_check 的超集:
- 检查 parent/child relationship;
- 检查缺失 downlink;
rootdescend=true为每个 leaf tuple 从 root 重新搜索;- 可结合 heapallindexed/checkunique。
代价:
这会阻止 concurrent INSERT/UPDATE/DELETE,也阻止 relation 的 VACUUM 和其他
utility command。锁只在函数运行期间持有,不是整个外部 transaction 都持有;但大型
索引检查时间可能很长,所以仍需要窗口。
它不能在 hot standby 上执行;bt_index_check 可以。不要在 replica 迁移脚本里把两者
当同义函数。
rootdescend 不一定是最有价值的生产检查
rootdescend 最初也服务于 B-tree feature development。它可能显著增加资源和时间,
但对现实中某些损坏类型的额外检出价值有限。
分层策略:
不要因为参数叫“更彻底”就每天全库打开。
不止 B-tree
PostgreSQL 18 amcheck 还提供:
verify_heapam 检查 table/sequence/materialized view 的物理格式和逻辑结构,返回每个
发现问题的 block/offset/attribute/message。
边界:
check_toast=true很慢;- TOAST 或其 index 损坏时,检查 toast value 理论上可能导致 server crash,很多情况 会只报错,但不能当无风险;
skip=all-visible/all-frozen降低成本,也降低覆盖;startblock/endblock可做 chunk;- 依赖的内部设施本身损坏时,函数可能无法继续。
对疑似 heap corruption,先按事故窗口、备份和证据保全运行,不要把全表
verify_heapam(check_toast=true) 当 cron。
调试日志
交互诊断可:
会显示更多检查上下文。生产自动化默认不应把 DEBUG 细节写入公开日志;错误信息可能 泄漏数据结构或可推断内容。
权限不是“能执行即可”
amcheck function 可授权给非超级用户,但官方文档提示安全与隐私风险。独立维护角色应:
Pigsty managed cluster 可用专门运维角色和私有日志采集;不要让普通应用角色在任意表上 调用结构取证函数。
28.6.2 heapallindexed、锁与业务窗口
heapallindexed 回答更强的问题
普通 B-tree structure check 主要从 index 看 index。heapallindexed=true 增加:
每一个应该有 index entry 的 heap tuple,是否都能在目标 index 的摘要结构中找到?
内部类似一次“dummy CREATE INDEX CONCURRENTLY”:
这能发现 heap/index 不一致,而这种 cross-check 不会在普通 index scan 中自动完成。
它是概率摘要
摘要受 maintenance_work_mem 限制。PostgreSQL 官方说明,为使每个应被索引的 heap
tuple 漏检不一致的概率不超过约 2%,近似需要每 tuple 2 bytes memory;更少内存时,
漏检概率缓慢上升。
因此:
仍不是数学上的 100% 证明。例行重复检查会给单个缺失/畸形 tuple 新的发现机会。
计划 memory:
只是质量量级,不是固定 allocation。对于 1 billion tuples,2 GB 量级已超过许多默认
maintenance_work_mem;不能以为 boolean 参数零成本。
heapallindexed 不改变 relation lock mode
对同一个函数:
但运行时间和 I/O 通常增加数倍,锁持有时长随之增加。锁 mode 没变,不等于业务 影响没变。
业务窗口要看四个预算
若使用 parent check,再明确:
pg_amcheck 批量编排
PostgreSQL client utility:
更深:
注意:
--parent-check/--rootdescend会用更强 relation locks;--rootdescend隐式选择 parent check;--jobs是并发连接,直接放大 server I/O/CPU/lock impact;- 默认选 table 时也检查 dependent B-tree indexes 和 TOAST;
--install-missing会改变数据库,应纳入 extension change,而不是巡检时隐式执行;- PostgreSQL 18 的
pg_amcheck文档说明该工具面向 PostgreSQL 14+ server; - client/server 版本和 option 集必须在 automation 中记录。
不要一次扫全库最大并发
较稳妥:
对象优先级:
同一时段避免叠加:
结果模型
每个对象至少记录:
“cron exit 0”没有对象级 coverage,无法证明哪些 relation 被跳过。
正式实验
一次性 churn_pkey:
表仅 50,000 live rows、无 concurrent business writes,时间只用于证明流程,不能作为 生产吞吐基准。实验在检查后还执行 concurrent reindex,并验证:
完整性检查与重建后 catalog 状态形成闭环。
28.6.3 amcheck 不替代数据页 checksum 和备份恢复
四种证明覆盖不同问题
| 机制 | 主要回答 | 不能回答 |
|---|---|---|
| data checksum | 读到的数据页 bytes 是否匹配上次写入 checksum | B-tree 排序/heap-index 逻辑、可恢复性 |
amcheck |
relation/access-method 结构与部分逻辑对应是否一致 | 所有磁盘 bytes、所有业务语义、备份可用 |
| backup manifest verification | 备份文件/size/checksum/所需 WAL 是否匹配 manifest | server 一定能恢复、业务结果正确 |
| restore drill | 能否启动、replay、打开并验证数据/SLO | 所有未来故障都可恢复 |
这四层是相加,不是互相替代。
data checksum 的边界
PostgreSQL 18 默认启用 page checksum,但可被 cluster-level 禁用。确认:
启用时:
- data page 写入时更新 checksum;
- 每次读取 page 时验证;
- 只保护 data pages;
- 不覆盖内部数据结构和 temporary files;
- cluster 级启停,不是 per-table。
page 已在 shared buffer 时,amcheck 可能检查的是 buffer 中版本,不一定在该时刻重新读 filesystem;它若触发磁盘读且 checksum 失败,也可能报 checksum error。
第 28 章 run 记录:
但这仍不意味着 storage、RAM 或所有 page 被本次实验读过。
amcheck 能发现 checksum 看不到的东西
例如:
checksum 只知道 bytes 是否与写入时一致;错误逻辑也可以被“正确”地写入并拥有有效 checksum。
备份验证也不是恢复
pg_verifybackup 可:
- 读 backup manifest;
- 检查 system identifier/manifest checksum;
- 对比缺失、额外、size 不同文件;
- 比较 file checksum;
- 对 plain backup 解析恢复所需 WAL。
官方文档同样明确:
即使 verify 通过,也应做 test restore,并验证数据库可运行、数据正确。
原因:
Pigsty 的 pgBackRest backup/PITR 流程应同时产出 repository check、restore drill 和业务 验收;第 31 章会完整展开恢复证明。
amcheck 通过不等于“备份健康”
可能:
也可能:
还可能:
需要按 failure domain 分别检查。
发现 corruption 后不要立即“修”
第一反应不应是:
先:
如果确认只有可重建 secondary index 损坏,reindex 可能合理;如果 heap、TOAST、system catalog 或 multiple copies 损坏,简单 reindex 可能失败或掩盖证据。
转入:
不同结果的安全路由
| 结果 | 动作 |
|---|---|
| check pass | 记录 coverage/time/version;继续其他层 |
| lock timeout | 本轮 inconclusive;换窗口,不算 pass |
| query canceled/resource gate | inconclusive;缩 scope/jobs |
| checksum failure | corruption incident;保全证据 |
| amcheck invariant error | corruption incident;定位 heap/index |
| server crash during deep check | highest severity;停止重复触发 |
| invalid index only | 依赖/heap 验证后评估 concurrent reindex |
| backup verify pass | 仍做 restore drill |
例行计划
频率应按数据变化量和 failure risk,不按“每月一号”机械套用。
完整性 SLO
这样“例行检查”才是可审计服务,不是散落脚本。
本节检查清单
延伸阅读
- PostgreSQL 18:amcheck
- PostgreSQL 18:pg_amcheck
- PostgreSQL 18:Data Checksums
- PostgreSQL 18:pg_verifybackup
- PostgreSQL 18:Backup Manifest Format
上一节:分区生命周期 · 返回本章目录 · 下一节:实战:建立维护节奏 · 查看全书目录 · 查看索引中心