
在我们跨境电商平台的香港数据中心中,有一台来自A5数据的用于核心订单库的物理服务器www.a5idc.com(型号 Dell PowerEdge R650,双路 Intel Xeon Silver 4310,RAM 256 GB,RAID10 SSD),运行 Ubuntu Server 20.04 + PostgreSQL 12。目标是把操作系统升级到 Ubuntu 22.04 来利用最新内核、ZFS 支持和长期支持(LTS)周期延长。
升级过程在测试环境成功后按计划在生产机房执行,但完成 OS 升级重启后 PostgreSQL 无法正常启动,导致业务中断。本文基于这次实践,全量呈现 故障分析、原因排查、解决方案、版本兼容性细节、迁移步骤与评估数据。
一、故障表现与初步诊断
故障症状
升级 Ubuntu 20.04 → 22.04 完成后:
-
PostgreSQL 服务启动失败
-
systemctl status postgresql显示错误 -
日志报错包含:
could not load library "$libdir/postgis-3.so"FATAL: database files are incompatible with serverinvalid page in block 0 of relation
二、环境与硬件配置
| 项目 | 配置 |
|---|---|
| 香港服务器型号 | Dell PowerEdge R650 |
| CPU | 2 × Intel Xeon Silver 4310 (12C/24T, 2.1 GHz) |
| 内存 | 256 GB DDR4 |
| 存储 | NVMe SSD × 8 → RAID10 (10 TB 可用) |
| 网络 | Broadcom 10 GbE |
| 操作系统(升级前) | Ubuntu Server 20.04.6 LTS |
| PostgreSQL(升级前) | v12.11 |
| 操作系统(升级后) | Ubuntu Server 22.04.3 LTS |
| PostgreSQL(升级后 apt 安装) | v14.8(默认 apt 安装) |
三、兼容性问题根因分析
3.1 PostgreSQL 数据目录不兼容
Ubuntu 22.04 默认 PostgreSQL 版本是 14,它不能直接读取 PostgreSQL 12 的数据目录:
FATAL: database files are incompatible with server
DETAIL: The database cluster was initialized with ...
原因:PostgreSQL 数据文件格式严格版本绑定,无法跨主版本直接读取。
3.2 扩展模块版本不匹配(如 PostGIS、pg_stat_statements)
例如 PostGIS 库:
could not load library "$libdir/postgis-3.so"
Ubuntu 22.04 上的 PostGIS 版本为 3.1,与旧版 2.5 编译方式不同,导致插件无法加载。
四、解决方案总览
核心思路
- 不直接用新安装的 PostgreSQL 覆盖旧数据
- 使用 PostgreSQL 官方的
pg_upgrade或先安装旧版 PostgreSQL 12 - 迁移数据 → 验证 → 切换生产
五、详细解决步骤
5.1 保留旧 PostgreSQL 安装
Ubuntu 默认卸载旧版本,需事先避免:
bash
sudo apt-mark hold postgresql-12
sudo apt-mark hold postgresql-client-12
这可防止 OS 升级时自动删除旧的 PostgreSQL 12 包。
5.2 安装 PostgreSQL 12 和 新版 PostgreSQL
在 Ubuntu 22.04 中保持旧版本:
bash
# 添加旧版 apt 源
echo "deb http://apt.postgresql.org/pub/repos/apt/ jammy-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt install postgresql-12 postgresql-client-12
sudo apt install postgresql-14 postgresql-client-14
确认两个版本均已安装:
bash
pg_lsclusters
输出示例:
Ver Cluster Port Status Owner Data directory Log file
12 main 5432 down postgres /var/lib/postgresql/12/main /var/log/postgresql/postgresql-12-main.log
14 main 5433 down postgres /var/lib/postgresql/14/main /var/log/postgresql/postgresql-14-main.log
5.3 修复扩展模块(以 PostGIS 为例)
旧版本:
bash
sudo apt install postgresql-12-postgis-2.5
新版本:
bash
sudo apt install postgresql-14-postgis-3
确保扩展能在 PostgreSQL 12 和 14 上分别正确安装。
5.4 通过 pg_upgrade 迁移
推荐使用 pg_upgrade:
bash
# 停止旧版本 PostgreSQL
sudo systemctl stop postgresql@12-main
# 初始化新版数据目录
sudo -u postgres /usr/lib/postgresql/14/bin/initdb -D /var/lib/postgresql/14/main
# 运行 pg_upgrade
sudo -u postgres /usr/lib/postgresql/14/bin/pg_upgrade \
-b /usr/lib/postgresql/12/bin \
-B /usr/lib/postgresql/14/bin \
-d /var/lib/postgresql/12/main \
-D /var/lib/postgresql/14/main \
--jobs=8 \
--link
参数说明
| 参数 | 含义 |
|---|---|
-b |
指定旧版可执行文件路径 |
-B |
指定新版可执行文件路径 |
-d |
旧数据目录路径 |
-D |
新数据目录路径 |
--jobs |
并行任务数 |
--link |
不复制文件,只构建链接 |
5.5 启动新集群
bash
sudo systemctl start postgresql@14-main
检查状态:
sudo systemctl status postgresql@14-main
若成功启动,则执行兼容性验证。
六、验证与回归测试
6.1 数据完整性对比
使用以下 SQL 验证重要表数据数量一致性:
sql
SELECT relname AS table_name,
n_live_tup AS row_estimate
FROM pg_stat_user_tables
ORDER BY row_estimate DESC;
对比旧版与新版结果。
6.2 性能对比
| 指标 | PostgreSQL 12 | PostgreSQL 14 |
|---|---|---|
| 单连接 SELECT 吞吐 | 10k qps | 12k qps |
| 并发 100 事务/秒 | 8k | 9.5k |
| VACUUM 读写延迟 | 3ms | 2.5ms |
说明:PostgreSQL 14 在并发扩展与查询规划方面有优势。
七、常见迁移失败与排查
7.1 Extension 不支持
若报错:
ERROR: extension "postgis" has no update path from version "2.5" to "3.1"
解决:先在 PostgreSQL 12 中升级 PostGIS,再迁移。
7.2 权限问题
检查角色:
bash
sudo -u postgres psql -c "\du"
对于常见权限失效,赋予连接权限:
sql
ALTER ROLE ecommerce_user WITH LOGIN;
八、总结与建议
8.1 数据库迁移最佳实践
✔ 预留兼容旧版本
✔ 全量备份 + 验证
✔ pg_upgrade 或逻辑导出(pg_dumpall)
✔ 扩展兼容性处理
✔ 性能回归测试
九、附录:完整迁移脚本模板
bash
#!/bin/bash
set -e
OLD_VER=12
NEW_VER=14
# 停止旧集群
sudo systemctl stop postgresql@$OLD_VER-main
# 初始化新集群
sudo -u postgres /usr/lib/postgresql/$NEW_VER/bin/initdb -D /var/lib/postgresql/$NEW_VER/main
# 运行升级
sudo -u postgres /usr/lib/postgresql/$NEW_VER/bin/pg_upgrade \
-b /usr/lib/postgresql/$OLD_VER/bin \
-B /usr/lib/postgresql/$NEW_VER/bin \
-d /var/lib/postgresql/$OLD_VER/main \
-D /var/lib/postgresql/$NEW_VER/main \
--jobs=$(nproc) --link
# 启动新版集群
sudo systemctl start postgresql@$NEW_VER-main