跳至内容
30.5 升级前检查与业务验证

30.5 升级前检查与业务验证

升级会复制或重新解释现有状态。源端已经存在的 invalid index、prepared transaction、 catalog 漂移或数据损坏,不会因为换了 major 自动痊愈;它们只会让故障因果更加难分。

升级前检查的目标不是证明数据库“完美”,而是建立一个已知、可恢复、可比较的起点。

30.5.1 系统目录、无效对象与长事务

第一组:cluster 与 database inventory

SELECT datname,
       pg_encoding_to_char(encoding) AS encoding,
       datlocprovider,
       datcollate,
       datctype,
       datlocale,
       datcollversion,
       datallowconn
FROM pg_database
ORDER BY datname;

SELECT spcname, pg_tablespace_location(oid)
FROM pg_tablespace
ORDER BY spcname;

SELECT rolname, rolsuper, rolreplication, rolbypassrls
FROM pg_roles
ORDER BY rolname;

随后逐 database 采集 extension、collation、schema、owner、privilege、large object、 publication/subscription、FDW/server/user mapping、database/role settings。pg_upgrade 处理的是整个 cluster,漏掉一个平时不连接的 database,也可能在 schema dump/restore 阶段让全局升级失败。

第二组:不完整与待完成状态

SELECT i.indexrelid::regclass AS index_name,
       i.indisready,
       i.indisvalid,
       i.indislive
FROM pg_index AS i
WHERE NOT i.indisready
   OR NOT i.indisvalid
   OR NOT i.indislive
ORDER BY 1;

SELECT conrelid::regclass AS relation,
       conname,
       contype,
       convalidated
FROM pg_constraint
WHERE NOT convalidated
ORDER BY 1, 2;

SELECT transaction, gid, prepared, owner, database
FROM pg_prepared_xacts
ORDER BY prepared;

invalid index 可能来自失败的 CREATE INDEX CONCURRENTLY;它是否删除、重建或保留, 要在升级前决定。NOT VALID constraint 可以是有意的渐进变更,但必须进入 inventory。 prepared transaction 是未完成的分布式业务事实,不能在停机时当普通长连接粗暴清掉。

还要检查 logical replication:

SELECT slot_name, slot_type, database, active,
       restart_lsn, confirmed_flush_lsn,
       wal_status, invalidation_reason
FROM pg_replication_slots
ORDER BY slot_name;

SELECT subname, subenabled, subslotname
FROM pg_subscription
ORDER BY subname;

PG17+ 的某些 logical slot/subscription 状态可由 pg_upgrade 迁移,但必须满足官方升级 前提;老版本、无效 slot 和复杂拓扑不能靠默认推断。

第三组:活动与变更冻结

SELECT pid, datname, usename, application_name,
       state, xact_start, backend_xid, backend_xmin,
       wait_event_type, wait_event
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_start;

正式停机前应:

冻结 DDL 与 extension 变更
停止 scheduler / ETL / schema migrator
drain application writers and long transactions
resolve prepared transactions by their coordinator
ensure replicas caught up
record final configuration and object manifest
stop old primary cleanly

pg_upgrade --check 还会检查 version、control data、prepared transaction、部分不支持 类型、required libraries、logical slot/subscription 等条件。它是必要门禁,但不覆盖 应用查询与业务不变量。

一个容易遗漏的原生限制是:pg_upgrade 不支持用户列使用若干保存 OID 的 reg* 类型,如 regcollationregconfigregprocregprocedureregclassregroleregtype 可升级。preflight 应按目标版本文档扫描,而不是等正式窗口报错。

30.5.2 amcheck、checksum 状态与备份恢复证据

在线先做结构检查

对已选重要 B-tree:

CREATE EXTENSION IF NOT EXISTS amcheck;

SELECT bt_index_check(
         index => i.indexrelid,
         heapallindexed => i.indisunique,
         checkunique => i.indisunique
       )
FROM pg_index AS i
WHERE i.indexrelid IN (
  'app.orders_pkey'::regclass,
  'app.orders_order_code_key'::regclass
);

bt_index_check 用与实际 index scan 相同的 operator class/comparison 逻辑验证 B-tree 结构与顺序;启用 heapallindexed 还会检查 heap tuple 是否有对应 index tuple。更强的 bt_index_parent_check 检查 parent/child invariant,但锁与成本更高。

大库不能在窗口前临时对所有 index 做最重检查。按:

系统目录和关键唯一索引
近期报错/存储异常对象
最大与最热对象
抽样普通对象

分层,并在生产克隆中测量耗时。失败时先转入第 35 章的数据抢救流程,不要把损坏对象 直接送进升级。

停库后再做全 cluster checksum 检查

pg_checksums --check --progress -D /pg/data/17

pg_checksums 要求 server clean shutdown;check 会扫描 cluster 文件,发现至少一个 checksum failure 时返回非零。启用/禁用 checksum 更是会修改数据块或 control file, HA 拓扑必须所有节点一致处理,不能把它顺手塞进 major upgrade。

PostgreSQL 18 initdb 默认开启 checksums,而 pg_upgrade 要求 old/new 设置匹配。 本章实验显式构造:

old PG17 checksums on
new PG18 checksums off

--check 返回 1;runner 删除这个不兼容的目标,重新以 checksums on 初始化,才允许 继续。

“有备份”必须变成一次可恢复证据

升级票据至少绑定:

backup_id: ...
source_system_identifier: ...
start_lsn: ...
stop_lsn: ...
timeline: ...
manifest_verified: true
required_wal_present: true
restore_target: isolated-pg17
restore_completed_at: ...
logical_manifest_equal: true
rto_measured: ...

pg_verifybackup 可以按 backup manifest 检查文件、摘要和所需 WAL,但官方明确说明它 不能覆盖启动 server 时的全部检查,仍需 test restore。Pigsty/pgBackRest 的 check、backup info 和 repository 健康也不能替代实际恢复。

回退到旧 cluster 依赖旧版本的可恢复性,所以升级前的 restore 应首先恢复为 old major。 升级后还要建立 new major 的新备份基线,并再次恢复;不能无限依赖切换前那份旧版本 备份。

30.5.3 查询结果、计划、性能和业务不变量基线

先比较结果,再讨论计划

一个新 major 选择了不同 plan,不一定是回归;选择了同一 plan,也不证明结果正确。 验证顺序:

  1. error/SQLSTATE 与行结果;
  2. 行数、排序、NULL、类型与精度;
  3. 业务不变量;
  4. plan shape、估算与实际行数;
  5. latency、吞吐、CPU、I/O、WAL 与锁。

pg_stat_statements、APM 和业务清单建立代表性 query corpus:

top total time
top mean/p99 latency
top calls
largest temp/WAL/I/O
关键交易与结算
低频但高风险管理语句
DDL / migration / backup / exporter queries

参数分布很重要。只用一个平均 customer_id 做 EXPLAIN,无法发现 skew、NULL、热点和 极端时间范围。

保存可比的证据格式

EXPLAIN (
  ANALYZE,
  BUFFERS,
  WAL,
  SETTINGS,
  VERBOSE,
  FORMAT JSON
)
SELECT ...;

对写 SQL 应在可回滚、隔离的克隆上执行。记录:

server version and system identifier
schema/data snapshot identity
session GUC
parameter values or distribution class
cold/warm cache condition
concurrency
plan JSON
result digest
latency distribution and resource counters

不要逐字比较 cost;新版本 cost model、节点和统计可能合理变化。设置门禁:

result: exact
business_invariants: zero violations
p95_latency: <= old * 1.15
p99_latency: <= old * 1.25
error_rate: no regression
temp_bytes: <= budget
wal_bytes_for_write_corpus: <= budget
plan_regression: reviewed, not byte-identical

本章 fixture 查询 order_code = 'order-5000' 在 PG17 和 PG18 都返回同一行,两个版本 都使用 orders_order_code_key Index Scan。实验记录完整 plan JSON,但 validator 只强制 业务结果相等和 plan 存在,不把“必须同一个节点”误写成普遍升级合同。

业务不变量是最终解释层

通用数据摘要之外,还应验证:

账务借贷平衡
订单状态合法且转移可达
租户间无交叉
外键孤儿为零
sequence next value 安全
任务水位和队列 offset 一致
权限矩阵不扩大
collation-sensitive pagination 稳定

这些规则应在 old snapshot 和 upgraded snapshot 上运行相同版本,并保存异常主键范围。 否则应用测试只能告诉你几个请求成功,不能证明整批数据仍满足业务语义。

30.5.4 amcheck 与 checksum 检测对象不同

证据主要检测不能证明
page checksumpage 从写入后是否发生可检测的物理变化SQL 逻辑、所有内存/写入前错误、索引业务一致性
pg_checksums --checkclean-shutdown cluster 中 checksum page 扫描未启用 checksum 的历史、server 可恢复、业务正确
amcheck B-treepage/link/order、可选 heap/index coverage 与 uniqueness所有 access method、业务行值、底层每个文件摘要
verify_heapamheap 结构异常并尽量继续报告所有 index、备份可恢复
backup manifest verify备份文件、摘要、所需 WAL 的一部分完整性server 启动和业务恢复
test restore备份能在声明环境恢复并启动之后每笔新写、全部应用语义
logical manifest行级规范化结果和业务聚合物理 page/index 结构

它们不是相互替代关系:

checksums pass + amcheck fails
  -> page 未被随机篡改,但 index 逻辑结构可能不满足 invariant

amcheck passes + checksum fails elsewhere
  -> 已检对象逻辑可读,但其他 page 物理完整性有问题

both pass + restore fails
  -> backup/WAL/config/secret/extension 链仍可能不完整

all pass + business invariant fails
  -> 数据在物理和结构层可读,业务事实仍然错误

升级发布至少需要这四条独立证据线:

physical integrity
structural integrity
recoverability
logical/business equivalence

把它们压成一个“数据库健康检查已通过”复选框,会丢掉每项证据真正覆盖的边界。


上一节:locale、collation 与索引风险 · 返回本章目录 · 下一节:用隔离环境完成升级彩排 · 查看全书目录 · 查看索引中心

最后更新于