方式2:使用 pg_upgrade --link(生产环境推荐、最快)
此方式可能无法正常启动容器,可使用方式3
这是 PostgreSQL 官方 Docker 团队从 18 开始特意设计的「面向未来」的升级路径。
前提:需要先把旧数据目录结构调整成新格式
最常用做法(目前推荐):
- 把旧数据移到新结构里(17 的数据放到
/var/lib/postgresql/17/docker)
- 把整个目录挂载到
/var/lib/postgresql
- 启动 18 容器先创建空的集群(会生成
/var/lib/postgresql/18/docker)
- 再用 pg_upgrade --link 快速升级
实战步骤(假设你用 docker compose):
# 先备份!!!
# 1. 停止旧容器
docker compose down
# 2. 假设你原来的 volume 挂载是 ./pgdata:/var/lib/postgresql/data
# 先手动调整目录结构(很重要!)
mkdir -p ./pgdata/17/docker
mv ./pgdata/* ./pgdata/17/docker/ # 把原来 data 里的全部内容移进去
# 3. 修改 docker-compose.yml
services:
db:
image: postgres:18
volumes:
- ./pgdata:/var/lib/postgresql # ← 注意!挂载整个 pgdata 到这里
# 可选:明确指定 PGDATA(通常可以省略)
# environment:
# PGDATA: /var/lib/postgresql/18/docker
# 4. 启动 18 容器(会自动创建空的 18/docker 目录)
docker compose up -d
# 5. 执行升级(最关键一步)
docker exec -it <新容器名> pg_upgrade \
--old-datadir=/var/lib/postgresql/17/docker \
--new-datadir=/var/lib/postgresql/18/docker \
--link # ← 关键参数!使用硬链接,几乎瞬间完成
# 6. 成功后可以删除旧目录(省空间)
# 在容器内执行:rm -rf /var/lib/postgresql/17/docker
# 7. 以后升级到 19、20 就会变得非常简单了!
额外自动化工具(更省事):
- 把旧数据移到新结构里(17 的数据放到
/var/lib/postgresql/17/data)
- 执行(可能出错)
docker run --rm \
-v ./postgres:/var/lib/postgresql \
tianon/postgres-upgrade:17-to-18 \
--link --verbose
出现错误:
old cluster does not use data checksums but the new one does
Failure, exiting
这是 PostgreSQL 18 版本的一个重大变更所导致的常见陷阱:
从 PostgreSQL 18 开始,initdb 默认启用数据校验和(data checksums)(用于检测磁盘损坏),而你旧的 PostgreSQL 17 集群很可能是在没有使用 -k / --data-checksums 参数的情况下初始化的(即 checksums = off)。这会导致 pg_upgrade 在检查时发现新旧集群的校验和设置不一致,从而直接失败。
pg_upgrade 不允许新旧集群的 checksum 设置不同(一个 on、一个 off),因为这属于磁盘存储格式的结构性变更。
解决方案 1:最简单、最推荐(适用于 tianon/postgres-upgrade:17-to-18)
在初始化新集群时关闭 checksums(使新旧集群都为 off),升级完成后再考虑开启。
tianon 镜像在初始化 PostgreSQL 18 时会使用默认设置(checksums on),因此需要显式强制关闭:
# 停止所有旧容器,并做好数据备份!!!
docker run --rm \
-v ./postgres:/var/lib/postgresql \
--env POSTGRES_INITDB_ARGS="--no-data-checksums" \ # ← 关键!强制关闭
tianon/postgres-upgrade:17-to-18 \
--link --verbose
- 如果你已经运行过一次(18/docker 目录已存在),请先删除或清空该目录(例如
rm -rf /path/to/your/host/pgdata/18/docker/*),然后再执行上述命令。
- 这样新集群就会以
--no-data-checksums 初始化,与旧集群的 off 状态一致,从而成功完成升级。
优点:最快、改动最少,仍可使用 --link 模式(几秒到几分钟即可完成)。
缺点:新集群的 checksums 仍为 off(但你可以在升级后手动开启,见下文)。
解决方案 2:先在旧的 PostgreSQL 17 集群上启用 checksums(推荐长期使用)
PostgreSQL 13 及以上版本均提供 pg_checksums 工具,可以在离线状态(停止数据库服务)下为旧集群启用 checksums。此过程会重写所有数据页(对于大型数据库可能耗时较长,几 GB 到几十 GB 的数据可能需要数小时)。
操作步骤:
- 停止旧的
postgres:17 容器。
- 使用
postgres:17 镜像运行 pg_checksums 启用校验和(注意使用 -u postgres):
docker run --rm \
-v ./postgres/17/data:/var/lib/postgresql/data \
-u postgres \
postgres:17 \
pg_checksums --enable --progress --verbose
此命令会扫描并重写旧数据目录,启用 checksums。
执行完成后,可再次运行 pg_checksums --check 验证是否有错误。
然后再运行 tianon 升级命令(此时新旧集群均为 on,完美匹配):
docker run --rm \
-v /path/to/your/host/pgdata:/var/lib/postgresql \
tianon/postgres-upgrade:17-to-18 \
--link --verbose
优点:未来启用 checksums 可提升数据安全性(PostgreSQL 官方强烈推荐)。
缺点:若旧集群数据量大,耗时较长(建议先在测试环境验证)。