3. 迁移升级前的环境与资产盘点

数据库迁移或版本升级失败,很少是因为“命令敲错了”,更多是因为盘点不完整:某个业务库依赖了 postgres_fdw 外部表、某个定时任务在凌晨两点调用了一个即将被废弃的函数、连接池里还残留着指向旧主库的地址、逻辑复制槽因为长期未消费而堆积了数十 GB WAL。这些问题在切换前不暴露,切换后集中爆发。因此,盘点的目标不是“列个清单”,而是形成两份可执行产物:资产清单(有哪些对象、多少、多大、谁在用)和依赖图谱(谁依赖谁、断开会怎样)。

一、盘点范围:从实例到外部依赖的六个层次

建议按“实例 → 数据库 → 模式 → 对象 → 扩展 → 外部依赖”自顶向下逐层收敛,避免遗漏。每一层都要记录对象数量、体积、访问频率和责任人。

  • 实例层:版本号(SHOW server_version)、架构(x86/ARM)、字符集与排序规则(SHOW server_encoding; SHOW lc_collate;)、时区、最大连接数、共享内存与 WAL 相关参数。排序规则在跨大版本升级时尤其关键,glibc 版本变化可能导致索引顺序不一致。
  • 数据库层:库名、大小、Owner、是否允许连接、默认表空间、是否有 template 定制。
  • 模式层:schema 列表、权限归属、是否有同名 schema 分属不同角色。
  • 对象层:表、分区表、索引、视图、物化视图、序列、函数、触发器、自定义类型、域、规则、事件触发器。
  • 扩展层:已安装扩展及其版本,是否为目标版本所支持。
  • 外部依赖层:FDW、dblink、逻辑复制、物理复制、外部程序调用、连接池、定时任务、监控与备份工具。

二、用系统目录快速产出对象清单

手工点开客户端逐个数对象不可靠,直接用系统目录查询。下面这组语句可作为盘点脚本的基础,输出结果建议导出为 CSV 存档,作为升级前后的对比基线。

-- 各库大小与连接情况
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size,
       datallowconn, datistemplate
FROM pg_database ORDER BY pg_database_size(datname) DESC;

-- 按 schema 统计对象数量
SELECT schemaname,
       count(*) FILTER (WHERE relkind='r')  AS 普通表,
       count(*) FILTER (WHERE relkind='p')  AS 分区表,
       count(*) FILTER (WHERE relkind='i')  AS 索引,
       count(*) FILTER (WHERE relkind='v')  AS 视图,
       count(*) FILTER (WHERE relkind='m')  AS 物化视图
FROM pg_stat_user_tables t
JOIN pg_class c ON c.oid = t.relid
JOIN pg_namespace n ON n.oid = c.relnamespace
GROUP BY schemaname ORDER BY 2 DESC;

-- 已安装扩展及版本
SELECT extname, extversion, nspname
FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace;

-- 大对象与序列使用情况
SELECT count(*) AS 大对象数 FROM pg_largeobject_metadata;
SELECT schemaname, sequencename, last_value
FROM pg_sequences WHERE last_value IS NOT NULL ORDER BY last_value DESC LIMIT 20;

查询结果中要特别关注三类“隐性资产”:一是无主对象(Owner 是已离职账号),迁移后可能无人有权修改;二是超大单表(超过 100 GB 的表在逻辑迁移中会显著拉长停机窗口);三是序列当前值,逻辑迁移若只搬数据不搬序列,切换后插入会立刻主键冲突。

三、扩展与外部依赖:最容易被低估的风险点

扩展的风险在于“版本绑定”。例如 postgis 2.x 升级到 3.x 存在函数签名变更,pg_stat_statements 需要随大版本重新编译,pg_cron、pg_partman 等则依赖后台 worker 与共享库预加载。盘点时要记录:扩展名、当前版本、目标版本是否提供、是否需要 shared_preload_libraries、是否有数据需要 ALTER EXTENSION ... UPDATE。

外部依赖则要画出“谁连我、我连谁”。常见的有:

  • FDW / dblink:本库通过外部表访问其他库,迁移后连接串、认证方式可能失效。
  • 复制链路:物理流复制(pg_stat_replication)、逻辑复制(pg_replication_slots、pg_stat_subscription)。逻辑复制槽若在切换前未消费完,会持续保留 WAL。
  • 连接池:PgBouncer、Pgpool 的配置中写死了后端地址与端口,切换时需同步更新并重载。
  • 定时任务:crontab、pg_cron、外部调度平台(如 Airflow)中调用的存储过程与函数。
  • 监控与备份:Prometheus exporter、pgBackRest、WAL-G 的仓库路径与保留策略。

四、权限体系与依赖图谱

权限盘点要落到“角色—对象—权限”三元组。重点检查:角色继承关系(pg_auth_members)、默认权限(ALTER DEFAULT PRIVILEGES)、行级安全策略(RLS)、以及通过 SECURITY DEFINER 提权的函数。后者在迁移中常被忽略,一旦函数 Owner 变更,权限模型就会整体偏移。

依赖图谱的核心是回答“如果我停掉这个对象,谁会受影响”。可以用 pg_depend 查询视图、物化视图、函数之间的依赖,再叠加应用侧调用关系。下面这张图给出了一次典型盘点的依赖关系示意:

迁移前资产依赖图谱(示例) 实线=直接依赖 红色虚线=跨库/外部依赖 标注为盘点关注点 业务应用 JDBC / ORM 连接池 PgBouncer 后端地址需同步更新 定时任务 pg_cron 调用存储过程 监控 / 备份 exporter / pgBackRest PostgreSQL 实例 版本 / 字符集 / 排序规则 业务库 appdb 表 / 分区 / 索引 / 序列 扩展 postgis / pg_stat_statements 角色与权限 RLS / SECURITY DEFINER 复制链路 物理流复制 / 逻辑槽 大对象 / 序列当前值 易遗漏,需单独导出 外部库 extdb postgres_fdw 外部表 从库 / 订阅端 逻辑复制槽积压 WAL 外部调度平台 Airflow / crontab 对象存储 / 备份仓库 WAL-G / S3 路径 盘点关注点: ① 连接池后端地址与端口 ② 逻辑复制槽积压量 ③ 扩展版本兼容性 ④ 序列当前值与大对象 ⑤ SECURITY DEFINER 函数 Owner ⑥ 定时任务调用的函数签名 ⑦ 备份仓库路径与保留策略

五、形成可执行的资产清单

盘点结果最终要落到一张可填写的清单上,字段不宜过多,但必须能支撑切换决策。下面给出一个可直接使用的示例结构(数值为示例假设,非实测):

类别对象/依赖关键信息迁移影响责任人
实例PostgreSQL 12.8UTF8 / en_US.UTF-8目标 16 需核对排序规则DBA-张
数据库appdb(约 420 GB)Owner: app_owner逻辑迁移停机窗口约 3 小时DBA-张
扩展postgis 2.5.5目标需 3.4函数签名变更,需回归测试开发-李
扩展pg_stat_statements 1.7需预加载大版本需重新编译DBA-张
外部依赖postgres_fdw → extdb外部表 12 张连接串与认证需重建开发-王
复制逻辑槽 slot_app_sub积压 18 GB WAL切换前需消费或重建DBA-张
连接池PgBouncer 1.18后端指向 10.0.0.11:5432切换时需 reload 配置运维-赵
定时任务pg_cron 每日 02:00调用 sp_daily_rollup()函数签名需确认兼容开发-李
权限角色 app_rw / app_ro含 RLS 策略 6 条需整体导出重建DBA-张
盘点小技巧:把清单导出为 CSV 后,用 pg_dump --schema-only 生成一份结构快照,与清单交叉核对。凡是“清单里有、快照里没有”或“快照里有、清单里没有”的对象,都是需要人工确认的盲区。
常见遗漏:序列当前值、大对象(lo)、事件触发器、外部表的远端连接信息、以及 SECURITY DEFINER 函数的 Owner。这五类对象在逻辑迁移中默认不会被完整搬运,必须在切换前单独处理。

完成上述工作后,你会得到两份可交付物:一份是覆盖实例到外部依赖的资产清单,另一份是标注了关键依赖路径的图谱。它们不仅是迁移方案设计的输入,也是切换后回归验证的对照基线——升级完成后,用同样的查询再跑一遍,逐项比对对象数量、扩展版本、序列值与复制状态,就能快速判断迁移是否真正完整。