PostgreSQL 版本升級:pg_upgrade 與邏輯複寫滾動升級完整攻略 | PostgreSQL
2026/07/18
PostgreSQL 版本升級是生命週期管理中最關鍵的維運作業。本文涵蓋 pg_upgrade 的
--link與--copy模式、邏輯複寫滾動升級(接近零停機)、Extension 相容性驗證、升級後 ANALYZE 統計重建、回滾計畫與常見陷阱的完整攻略。
Major vs Minor 升級
PostgreSQL 版本號採用 Major.Minor 格式(如 16.3),兩種升級性質截然不同:
| 維度 | Minor 升級 | Major 升級 |
|---|---|---|
| 範例 | 15.3 → 15.6 | 15.x → 16.x |
| 資料格式 | 不變(Binary Compatible) | 可能變更(需遷移) |
| 升級方法 | 替換執行檔、重啟即可 | 需要 pg_upgrade 或邏輯複寫 |
| 停機時間 | 數分鐘 | 數分鐘至數小時 |
| 統計資訊 | 保留 | 需重建(ANALYZE) |
Minor 升級流程
# 停止服務
sudo systemctl stop postgresql@15-main
# 升級套件
sudo apt-get update
sudo apt-get install --only-upgrade postgresql-15
# 重新啟動
sudo systemctl start postgresql@15-main
# 驗證版本
psql -c "SELECT version();"
升級策略比較
三種主流 Major 升級策略:
| 策略 | 停機時間 | 技術複雜度 | 回滾難度 |
|---|---|---|---|
pg_upgrade --link | 分鐘級(5-30 分鐘) | 中 | 中 |
pg_upgrade --copy | 小時級(視資料量) | 中 | 低 |
| 邏輯複寫滾動升級 | 接近零停機 | 高 | 低 |
pg_dump + pg_restore | 數小時 | 低 | 極低 |
選擇指引:
資料庫 < 100GB,可接受數分鐘停機 → pg_upgrade --link
資料庫 > 100GB,不允許長時停機 → 邏輯複寫滾動升級
開發/測試環境 → pg_dump + pg_restore
磁碟空間不足 → pg_upgrade --link(零額外空間)
pg_upgrade 完整流程
pg_upgrade 是官方就地升級工具,透過解析舊資料目錄並在新版本叢集中重建系統目錄來完成升級。
升級前準備
# 1. 安裝新版本套件(不要移除舊版本)
sudo apt-get install postgresql-17 postgresql-server-dev-17
# 2. 初始化新版本叢集(locale 必須與舊叢集相同)
sudo -u postgres /usr/lib/postgresql/17/bin/initdb \
-D /var/lib/postgresql/17/main \
--locale=en_US.UTF-8 \
--encoding=UTF8
# 3. 確認新舊叢集都已停止
sudo systemctl stop postgresql@17-main
sudo systemctl stop postgresql@15-main
–check 預檢(Dry Run)
強烈建議在正式升級前執行 --check,不修改任何資料,僅報告問題:
sudo -u postgres /usr/lib/postgresql/17/bin/pg_upgrade \
--old-datadir /var/lib/postgresql/15/main \
--new-datadir /var/lib/postgresql/17/main \
--old-bindir /usr/lib/postgresql/15/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--check --verbose
預檢通過會顯示:*Clusters are compatible*
–link 模式(最快速)
使用 Unix 硬連結(Hard Link),升級時間幾乎不受資料庫大小影響:
sudo -u postgres /usr/lib/postgresql/17/bin/pg_upgrade \
--old-datadir /var/lib/postgresql/15/main \
--new-datadir /var/lib/postgresql/17/main \
--old-bindir /usr/lib/postgresql/15/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--link --verbose \
2>&1 | tee /var/log/pg_upgrade_$(date +%Y%m%d).log
重要風險:
--link模式下,一旦啟動新叢集並寫入資料,舊叢集的資料檔案將因硬連結共用而被修改,無法再用於回滾。
–copy 模式
逐檔複製資料目錄,較慢但舊目錄完整保留:
sudo -u postgres /usr/lib/postgresql/17/bin/pg_upgrade \
--old-datadir /var/lib/postgresql/15/main \
--new-datadir /var/lib/postgresql/17/main \
--old-bindir /usr/lib/postgresql/15/bin \
--new-bindir /usr/lib/postgresql/17/bin \
--jobs 4 --verbose
# --jobs:平行複製數,建議為 CPU 核心數的 50-75%
完整升級步驟
# === 升級前 ===
# 1. 備份(pg_upgrade 不能代替備份!)
pg_basebackup -D /backup/pre-upgrade-$(date +%Y%m%d) -Ft -z -P
# 2. 記錄所有 Extension 清單
psql -c "SELECT name, installed_version FROM pg_available_extensions
WHERE installed_version IS NOT NULL ORDER BY name;"
# 3. 確認沒有 prepared transactions
psql -c "SELECT * FROM pg_prepared_xacts;"
# 4. 確認 replication slots
psql -c "SELECT slot_name, active FROM pg_replication_slots;"
# 5. 停止應用程式流量
# === 升級執行 ===
# 6. 停止舊叢集
sudo systemctl stop postgresql@15-main
# 7. pg_upgrade --check
# 8. pg_upgrade --link(或 --copy)
# === 升級後 ===
# 9. 啟動新叢集
sudo systemctl start postgresql@17-main
# 10. 驗證
psql -c "SELECT version();"
psql -c "\l"
psql -c "\dx"
# 11. 重建統計資訊
sudo -u postgres ./analyze_new_cluster.sh
# 12. 確認正常後刪除舊叢集
sudo -u postgres ./delete_old_cluster.sh
邏輯複寫滾動升級
邏輯複寫(Logical Replication)允許在接近零停機的情況下完成 Major 版本升級。
架構
升級中(邏輯複寫同步期):
應用程式 ──► PG 15 Primary ──(Logical Replication)──► PG 17 New
切換瞬間(< 1 分鐘停機):
應用程式 ──────────────────────────────────────────► PG 17 Primary
在 PG15(舊 Primary)上操作
-- 1. 確認 wal_level = logical
SHOW wal_level;
-- 若為 replica,需修改 postgresql.conf 並重啟
-- 2. 建立複寫使用者
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'strong_password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO replicator;
-- 3. 建立 Publication
CREATE PUBLICATION upgrade_pub FOR ALL TABLES;
在 PG17(新叢集)上操作
# 4. 匯入 Schema(邏輯複寫不傳輸 DDL)
pg_dump --schema-only --no-owner --no-acl \
-h pg15-host mydb | psql -h pg17-host mydb
# 5. 建立 Subscription
psql -h pg17-host mydb << 'EOF'
CREATE SUBSCRIPTION upgrade_sub
CONNECTION 'host=pg15-host port=5432 dbname=mydb user=replicator password=strong_password'
PUBLICATION upgrade_pub
WITH (copy_data = true);
EOF
監控與切換
-- 6. 監控複寫落差
SELECT
slot_name,
pg_size_pretty(pg_current_wal_lsn() - confirmed_flush_lsn) AS lag_pretty
FROM pg_replication_slots
WHERE slot_name = 'upgrade_sub';
-- === 切換流程(lag 接近 0 時)===
-- 7. 停止寫入 PG15
ALTER SYSTEM SET default_transaction_read_only = on;
SELECT pg_reload_conf();
-- 8. 確認 PG17 完全追上(lag = 0)
-- 9. 同步 Sequence(邏輯複寫不同步 sequence)
-- 在 PG15 上產生同步指令
SELECT 'SELECT setval(''' || schemaname || '.' || sequencename || ''', ' || last_value || ');'
FROM pg_sequences;
-- 將結果在 PG17 上執行
-- 10. 更新應用程式連線指向 PG17
-- 11. 清理舊資源
-- PG17: DROP SUBSCRIPTION upgrade_sub;
-- PG15: DROP PUBLICATION upgrade_pub;
升級前檢查清單
Extension 相容性驗證
-- 查詢所有已安裝 Extension
SELECT name, default_version, installed_version
FROM pg_available_extensions
WHERE installed_version IS NOT NULL ORDER BY name;
常見 Extension 相容性(PG15 → PG17):
| Extension | 相容性 | 注意事項 |
|---|---|---|
pg_stat_statements | 完全相容 | 升級後需重建 |
postgis | 需版本升級 | PostGIS 3.4+ 支援 PG17 |
pg_trgm | 完全相容 | — |
hstore | 完全相容 | — |
timescaledb | 需版本升級 | 有專屬升級路徑 |
pgvector | 需確認 | 0.7.0+ 支援 PG17 |
# 在新版本上預先安裝 Extension 套件
sudo apt-get install \
postgresql-17-postgis-3 \
postgresql-17-pgvector
升級後任務
ANALYZE 統計資訊重建
pg_upgrade 不遷移統計資訊,新叢集的 Query Planner 會做出次優決策:
# 方法 1:執行 pg_upgrade 自動生成的腳本(推薦)
sudo -u postgres ./analyze_new_cluster.sh
# 等同於 vacuumdb --all --analyze-in-stages
# 方法 2:手動執行
vacuumdb --all --analyze-in-stages --jobs 4 --verbose
# analyze-in-stages 說明:
# Stage 1:最小統計(快速,立即可用)
# Stage 2:預設統計量
# Stage 3:完整統計量(最準確)
Extension 升級
-- 列出可升級的 Extension
SELECT name, installed_version, default_version
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
AND installed_version != default_version;
-- 升級特定 Extension
ALTER EXTENSION pg_stat_statements UPDATE;
ALTER EXTENSION postgis UPDATE TO '3.4.0';
其他升級後任務
# Physical Replica 必須重新建立
sudo -u postgres pg_basebackup \
-h primary-host \
-D /var/lib/postgresql/17/main \
-P -R --wal-method=stream
# 確認 pgBouncer / 連線池設定已更新
停機時間估算
pg_upgrade –link 模式
Total Downtime ≈ T_pre + T_upgrade + T_post
T_pre:停止流量 + 關閉連線 + 停服務 ~5 分鐘
T_upgrade:pg_upgrade 執行 ~5-15 分鐘(固定)
T_post:啟動新叢集 + 基本驗證 ~3-5 分鐘
典型停機窗口:10-30 分鐘
# 查詢資料庫大小(估算 --copy 模式時間)
psql -c "SELECT datname, pg_size_pretty(pg_database_size(datname))
FROM pg_database ORDER BY pg_database_size(datname) DESC;"
# --copy 模式速率約 1GB/分鐘
回滾計畫
pg_upgrade –link 模式回滾
# 情境 1:PG17 尚未啟動(最安全)
sudo systemctl start postgresql@15-main
# 情境 2:PG17 已啟動但尚未寫入新資料
sudo systemctl stop postgresql@17-main
sudo systemctl start postgresql@15-main
# 情境 3:PG17 已寫入新資料
# 必須從升級前的備份還原
pg_upgrade –copy 模式回滾(最乾淨)
# 舊目錄完整保留,直接啟動 PG15
sudo systemctl stop postgresql@17-main
sudo systemctl start postgresql@15-main
回滾決策流程
升級後發現問題
│
▼
問題嚴重程度?
├── 輕微(個別查詢問題)→ 優化查詢計畫,繼續使用新版本
├── 中等(Extension 問題)→ 修復 Extension,不需回滾
└── 嚴重(資料損壞/服務中斷)
│
▼
PG17 是否已寫入新資料?
├── 否 → 直接啟動 PG15
└── 是 → 從備份還原
PG16 → PG17 注意事項
-- PG17 新功能
-- COPY FROM ... RETURNING
COPY orders(id, amount) FROM stdin RETURNING id;
-- JSON_TABLE(完整 SQL/JSON 標準)
SELECT * FROM JSON_TABLE(
'{"orders": [{"id": 1}, {"id": 2}]}'::json,
'$.orders[*]' COLUMNS (id INT PATH '$.id')
);
-- WAL Summary 預設啟用
SHOW summarize_wal; -- PG17 預設 on
常見陷阱
| 陷阱 | 錯誤訊息 | 解法 |
|---|---|---|
| Replication Slot 未清除 | contains replication slots | 升級前 pg_drop_replication_slot() |
| locale 不一致 | lc_collate values do not match | initdb 時指定相同 locale |
| Extension 套件不存在 | Could not load library | 預先安裝 postgresql-17-xxx 套件 |
| Physical Replica 無法連線 | Binary 格式不相容 | 升級後重新 pg_basebackup |
| 升級後效能下降 | 統計資訊為空 | 執行 analyze_new_cluster.sh |
| 密碼格式不相容 | md5 → scram-sha-256 | 確認 password_encryption 設定 |
| prepared transactions 未清除 | contains prepared transactions | 升級前 COMMIT 或 ROLLBACK |
版本 EOL 時間表
| 版本 | 發布年份 | EOL 日期 |
|---|---|---|
| PG 13 | 2020 | 2025-11-13 |
| PG 14 | 2021 | 2026-11-12 |
| PG 15 | 2022 | 2027-11-11 |
| PG 16 | 2023 | 2028-11-09 |
| PG 17 | 2024 | 2029-11-08 |
建議:在 EOL 前至少 6 個月完成升級。
pg_upgrade支援跨版本升級(如 PG13 → PG17),但版本跨度越大,不相容風險越高。
總結
PostgreSQL 版本升級的核心要點:
- Minor 升級:替換執行檔 + 重啟,風險極低
- pg_upgrade –link:分鐘級停機,適合大部分場景
- pg_upgrade –copy:較慢但舊目錄保留,回滾最安全
- 邏輯複寫滾動升級:接近零停機,適合嚴格 SLA 環境
- 升級前必做:備份、
--check預檢、Extension 驗證 - 升級後必做:
analyze_new_cluster.sh、Extension 更新、Replica 重建 - 回滾計畫:升級前制定,確認每個場景的回滾路徑
下一篇,我們將探討 pgvector 向量搜尋——從安裝、向量資料類型到 HNSW/IVFFlat 索引的完整 AI 嵌入搜尋實戰。