PostgreSQL 主版本升级 (pg_upgrade / 逻辑复制)
PostgreSQL难度: 高级耗时: 1–3 小时(+ 取决于数据量)主版本升级(例如 16 → 17):选择方法(pg_upgrade --link / --copy / --clone,或通过逻辑复制实现近乎零停机的迁移)、pg_upgrade --check、扩展兼容性、迁移配置、重新收集统计信息、通过 rsync 或 pg_basebackup 重建 standby、删除旧集群以及回退 runbook。涵盖单机、流复制(primary + standby)和 Patroni 集群(Patroni 官方的主版本升级流程:停止 Patroni,在 leader 上执行 pg_upgrade,删除 DCS 记录,由 Patroni 重新克隆 replica)。适用于 RHEL 系列和 Debian/Ubuntu(包括 pg_upgradecluster)。
同一主版本内的更新(例如 17.4 → 17.6)不使用本指南,请使用 pg-minor-update。表级持续复制或部分迁移请使用 pg-logical-repl;用 dump 将小型数据库迁移到另一台服务器请使用 pg-dump-restore。
架构: 在同一服务器上原地升级(pg_upgrade;单机、primary + standby 或 Patroni 集群),或迁移到新服务器(逻辑复制)
场景
- 主版本升级 — 主版本升级(例如 16 → 17):预检查、备份、pg_upgrade 或逻辑复制、验证和清理。
- 回退(升级失败 / 放弃升级) — 升级中途停止,或新版本出现问题:回退到旧版本。使用 --link 时,如果新集群已经启动过,只能从备份恢复。
开始之前
- 源集群为 PostgreSQL …,从 PGDG 仓库安装(pg-install)。目标版本 … 高于源版本。
- 应用和驱动已在 PostgreSQL … 上测试;同样的升级已在测试环境中演练过。已阅读截至 … 的每个主版本 release notes 中的 "Migration to Version" 部分(已移除的参数、行为变更)。
- 所有已安装扩展都有适用于 … 的版本(PostGIS、pg_repack、pgaudit、TimescaleDB…)。如果某个扩展没有 … 版本,则无法升级。
- …新数据目录有与当前数据量相当的空闲空间。…新旧数据目录位于同一文件系统(硬链接的前提);有几 GB 空闲空间用于 WAL 和新的系统目录。…文件系统支持 reflink(XFS reflink=1 或 Btrfs),且两个目录位于同一文件系统。…
- 已列出现有的逻辑复制槽 / subscription:…源为 PostgreSQL 17+ 时,pg_upgrade 会迁移逻辑复制槽,但所有变更必须已被消费…源为 PostgreSQL … 时,pg_upgrade 不迁移逻辑复制槽;升级后需要重新创建…。
- primary 能以 postgres 用户免密 SSH 到每个 standby,且 standby 上已安装 rsync。数据目录布局为 …/<version>/data(rsync 使用共同的上级目录)。
- standby 处于 streaming 状态;primary 的 pg_hba.conf 允许复制连接,… 的密码在 standby 的 ~postgres/.pgpass 中。
- Patroni 集群健康:… 是当前 leader,所有 replica 均为 streaming 且无延迟,没有 Pending restart。集群名 …,配置 …,REST API 密码在 /etc/patroni/patroni.env 中(pg-patroni 布局)。已对 DCS (etcd) 做快照备份。升级期间集群将完全停止:经 HAProxy/VIP 的连接会被拒绝。
- Patroni 版本支持 PostgreSQL …(见 Patroni release notes 中对新主版本的支持)。旧版 Patroni 可能无法识别新版本的参数和控制文件;必要时先滚动升级 Patroni。
- 目标服务器已用 pg-install 安装 PostgreSQL …,为空且正在运行;已安装源端扩展的 … 版本。目标能访问源端的 … 端口。
- 要迁移的表有主键或 replica identity(否则源端的 UPDATE/DELETE 会报错)。为列出的表添加主键,或执行 ALTER TABLE … REPLICA IDENTITY FULL。
- 有一份最新且已验证可恢复的备份… (pgBackRest)…;如有可能,还将做服务器/磁盘快照。… 使用 --link 方法时,新集群启动后这是唯一的回退途径。…
指南阶段
- 预检查 — 检查 Patroni 集群状态 · 记录集群属性 · 列出扩展的 RPM/deb 包 · 检查 prepared transaction 和长事务
- 准备新集群 — 创建新数据目录 · 用相同属性创建新集群 (initdb) · 将配置迁移到新集群 · 检查新版本中是否有无效参数 · 按新版本检查 Patroni 参数
- 备份与回退点 — 备份 (pgBackRest full) · 备份 (pg_dumpall + 快照)
- 兼容性检查 (pg_upgrade --check) — pg_upgrade --check
- 停机:停止旧集群 — 停止应用 · 停机开始时的最后备份 (pgBackRest incr) · 停机开始时的最后逻辑备份 · 停止旧 primary · 在 replica 上停止 Patroni · 在 leader 上停止 Patroni …
- 升级 — 运行 pg_upgrade · 用 pg_upgradecluster 升级 · 将 conf.d 文件迁移到新集群 · 将 pgBackRest 配置切换到新集群 · 将 patroni.yml 中的 bin_dir 和 data_dir 改为新版本 · 将 pgBackRest stanza 升级到新版本 …
- 启动新集群 — 启动新集群 · 在 leader 上启动 Patroni · 立即启动 standby
- 升级后操作 — 更新扩展 · 收集统计信息 · pgBackRest 检查和新的全量备份
- 验证 — 版本、参数和数据库 · 验证 Patroni 集群 · 检查日志 · 应用测试
- 删除旧集群 — 删除旧数据目录 · 删除… replica … standby …上的旧数据目录 · 删除旧版本的软件
- 准备源服务器 — wal_level = logical · 重启源端 · 创建复制用户 · 将目标添加到 pg_hba.conf · 重新加载 pg_hba · 创建 publication
- 目标端的 schema 和订阅 — 将源端密码写入 .pgpass · 复制角色 (globals) · 复制 schema · 创建 subscription · 等待初始复制完成 · 检查延迟
- 最终切换 (cutover) — 在源端停止写入 · 确认最后的变更已传输 · 传输 sequence 值 · 删除 subscription · 收集统计信息 · 将应用切换到目标端 …
- 回退 — 停止新集群 · 在 replica 上停止 Patroni · 在 leader 上停止 Patroni · 恢复 pg_control(--link,新集群未启动) · …从备份回退(--link,新集群已启动)…从逻辑备份回退(--link,新集群已启动)… · 不经 Patroni 完成恢复 …