Postgres 数据库迁移:把复杂谜题拆成可验证的步骤

2026-08-26 35 预计阅读时间: 1 分钟
来源: percona.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

Postgres 正被用于替换旧系统、支撑新的绿色项目,也开始成为智能代理系统的后端。相比数据库本身的创新应用,迁移往往显得乏味,却经常因为数据量、版本差异、权限和停机窗口而变成一场不该如此困难的排障工作。

解决迁移问题的关键,不是寻找一条“万能命令”,而是把过程拆成可以检查、回滚和重复执行的阶段:盘点源库,设计目标环境,完成备份与恢复,验证数据和应用行为,最后再切换流量。

迁移前:先确认你到底要搬什么

一次 Postgres 迁移通常包含几类对象:

  • 表、索引、序列和约束等数据对象
  • 函数、触发器和扩展
  • 角色、权限和默认权限
  • 数据本身,以及大对象等特殊内容
  • 应用连接配置、连接池和监控配置

不能只用“数据库大小”评估工作量。一个体积不大的数据库,如果依赖多个扩展、复杂函数或严格的权限模型,迁移风险可能高于一个只有普通表和索引的大库。

可以先在源库收集基础信息:

SELECT current_setting('server_version') AS server_version,
       current_database() AS database_name,
       current_user AS current_user;

SELECT extname, extversion
FROM pg_extension
ORDER BY extname;

SELECT schemaname,
       relname,
       pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;

这些查询不能替代完整的迁移评估,但能快速暴露版本、扩展和大表等关键变量。

选择迁移方式:逻辑迁移还是物理迁移

常见方案可以按目标分成两类。

逻辑备份与恢复

pg_dumppg_restore 适合需要跨版本迁移、调整对象结构,或者只迁移部分数据库对象的场景。它们生成的是逻辑表示,恢复时会重新创建表、索引和其他对象。

优点是灵活、可读性和可操作性较好;代价是大型数据库恢复时间可能较长,而且需要处理角色、扩展和权限等数据库外部依赖。

物理备份或复制

物理方案复制数据库文件或整个实例状态,通常更适合同一数据库版本体系内的大规模迁移,以及对停机时间要求较高的场景。它对环境、架构和版本兼容性更敏感,不能简单地把数据目录复制到任意目标环境。

实际选择时,至少要回答三个问题:

  1. 是否需要跨 Postgres 大版本迁移?
  2. 可以接受多长的只读或停机窗口?
  3. 目标环境是否必须重构角色、存储或网络配置?

如果答案偏向跨版本和结构调整,逻辑迁移通常更容易控制;如果数据量很大且环境高度兼容,物理方案可能更适合。具体能力仍需根据使用的 Postgres 版本和托管平台确认。

一个可改造的逻辑迁移流程

下面的命令展示了一个基础流程。示例假设源库和目标库都可以通过网络访问,并使用环境变量传递密码。执行前请替换连接地址、端口、数据库名和备份目录。

#!/usr/bin/env bash
set -euo pipefail

SRC_HOST="source-db.example.com"
SRC_PORT="5432"
SRC_DB="appdb"
SRC_USER="migration_user"
BACKUP_DIR="./postgres-migration"
DUMP_FILE="$BACKUP_DIR/appdb.dump"

DST_HOST="target-db.example.com"
DST_PORT="5432"
DST_DB="appdb"
DST_USER="restore_user"

mkdir -p "$BACKUP_DIR"

# 1. 生成适合并行恢复的自定义格式备份
pg_dump \
  --host "$SRC_HOST" \
  --port "$SRC_PORT" \
  --username "$SRC_USER" \
  --dbname "$SRC_DB" \
  --format custom \
  --file "$DUMP_FILE"

# 2. 在目标库创建扩展或预置依赖,具体内容按应用实际情况调整
psql \
  --host "$DST_HOST" \
  --port "$DST_PORT" \
  --username "$DST_USER" \
  --dbname "$DST_DB" \
  --command 'CREATE EXTENSION IF NOT EXISTS pgcrypto;'

# 3. 恢复数据和数据库对象;并行度应根据目标资源压测后设置
pg_restore \
  --host "$DST_HOST" \
  --port "$DST_PORT" \
  --username "$DST_USER" \
  --dbname "$DST_DB" \
  --jobs 4 \
  --verbose \
  "$DUMP_FILE"

在生产环境中,角色和全局对象通常需要单独处理,因为 pg_dump 默认针对单个数据库,不会完整导出整个实例的角色定义。可以根据权限审计结果使用 pg_dumpall --globals-only 导出全局对象,再经过审核后导入目标环境。不要把生产密码直接写入脚本;PGPASSWORD 只是简单示例,更稳妥的做法是使用 .pgpass、密钥管理服务或平台提供的凭据注入机制。

如果源库在备份期间仍持续写入,单次逻辑备份和应用切换之间可能出现数据差异。对于要求低停机的迁移,需要额外设计持续复制、变更捕获或短暂写入冻结方案,不能仅靠一次 pg_dump 解决。

验证比“恢复成功”更重要

pg_restore 没有报错,只能说明恢复过程完成了,不能证明应用已经可用。建议至少做四层验证。

结构验证

对比源库和目标库的表、索引、序列、扩展、函数及约束。尤其要检查目标库是否意外缺少应用依赖的扩展,或因为权限不足而跳过对象。

数量与关键数据验证

对核心表比较行数,并对关键业务范围执行抽样查询。对于允许的场景,可以为稳定字段计算摘要或校验值;不要只比较数据库总大小,因为压缩、统计信息和存储布局会影响大小。

SELECT 'orders' AS table_name, count(*) AS row_count
FROM public.orders
UNION ALL
SELECT 'users', count(*)
FROM public.users;

应用验证

让应用连接目标库执行真实的读写路径:登录、创建订单、更新状态、后台查询和定时任务都应纳入检查。还要验证连接池的 SSL、超时、重试和最大连接数配置,因为数据库迁移后,网络路径和连接限制可能已经改变。

性能与运维验证

检查慢查询、锁等待、错误日志、CPU、内存、磁盘和连接数。恢复后重新收集统计信息可能是必要的,但具体操作应结合数据库版本和托管环境安排。迁移完成前,必须确认备份、监控、告警和恢复流程已经指向目标库。

把切换设计成一次受控变更

一个可操作的切换流程通常包括:

  1. 提前冻结模式、备份并演练恢复。
  2. 降低 DNS 或服务发现缓存时间,但要考虑客户端和连接池实际缓存行为。
  3. 暂停写入或启用增量同步,追平最后一批变更。
  4. 执行最终校验,并切换应用连接配置。
  5. 观察错误率、延迟、连接数和业务指标。
  6. 在约定的观察窗口内保留源库,确认无误后再处理下线。

回滚也必须提前写清楚。数据库已经接受新写入后,直接把连接字符串切回旧库可能造成数据分叉。因此,回滚策略要说明哪些写入可以丢弃、如何反向同步,以及在什么条件下只能进入人工恢复流程。

迁移检查清单

  • [ ] 已确认源、目标 Postgres 版本和扩展兼容性
  • [ ] 已盘点角色、权限、函数、触发器和特殊对象
  • [ ] 已完成备份并在独立环境验证恢复
  • [ ] 已测量备份、恢复和最终同步耗时
  • [ ] 已准备数据、结构、应用和性能验证 SQL
  • [ ] 已验证目标库的监控、告警、备份和连接配置
  • [ ] 已明确写入冻结窗口、切换步骤和回滚边界
  • [ ] 已安排迁移后的观察窗口和责任人

Postgres 迁移之所以容易变成“谜题”,往往不是因为某条命令特别复杂,而是因为数据、权限、版本、应用和运维边界没有被同时纳入设计。把迁移当作一组可验证的工程步骤,先演练,再切换,才能让这项看似普通的工作真正变得可控。


相关推荐