PostgreSQL 版本升級:pg_upgrade 與邏輯複寫滾動升級完整攻略 | PostgreSQL

2026/07/18
PostgreSQL 版本升級:pg_upgrade 與邏輯複寫滾動升級完整攻略 | PostgreSQL

PostgreSQL 版本升級是生命週期管理中最關鍵的維運作業。本文涵蓋 pg_upgrade--link--copy 模式、邏輯複寫滾動升級(接近零停機)、Extension 相容性驗證、升級後 ANALYZE 統計重建、回滾計畫與常見陷阱的完整攻略。

Major vs Minor 升級

PostgreSQL 版本號採用 Major.Minor 格式(如 16.3),兩種升級性質截然不同:

維度Minor 升級Major 升級
範例15.3 → 15.615.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*

使用 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 / 連線池設定已更新

停機時間估算

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/分鐘

回滾計畫

# 情境 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 matchinitdb 時指定相同 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 1320202025-11-13
PG 1420212026-11-12
PG 1520222027-11-11
PG 1620232028-11-09
PG 1720242029-11-08

建議:在 EOL 前至少 6 個月完成升級。pg_upgrade 支援跨版本升級(如 PG13 → PG17),但版本跨度越大,不相容風險越高。


總結

PostgreSQL 版本升級的核心要點:

  1. Minor 升級:替換執行檔 + 重啟,風險極低
  2. pg_upgrade –link:分鐘級停機,適合大部分場景
  3. pg_upgrade –copy:較慢但舊目錄保留,回滾最安全
  4. 邏輯複寫滾動升級:接近零停機,適合嚴格 SLA 環境
  5. 升級前必做:備份、--check 預檢、Extension 驗證
  6. 升級後必做analyze_new_cluster.sh、Extension 更新、Replica 重建
  7. 回滾計畫:升級前制定,確認每個場景的回滾路徑

下一篇,我們將探討 pgvector 向量搜尋——從安裝、向量資料類型到 HNSW/IVFFlat 索引的完整 AI 嵌入搜尋實戰。

BenZ Software Developer

熱愛技術的軟體開發者,在這裡分享程式開發經驗與學習筆記。