背景進程:PostgreSQL 的幕後工作者們 | PostgreSQL
PostgreSQL 是多進程架構,除了處理客戶端請求的 Backend Process,還有一批背景進程(Background Processes)負責維護資料庫的健康。理解 Postmaster、Checkpointer、Background Writer、Autovacuum 等進程的職責、觸發條件與調校參數,是效能調校與穩定性維護的核心知識。
Postmaster:萬物之起點
Postmaster 是 PostgreSQL 的主進程,負責:
- 監聽連線:預設監聽 TCP port 5432(及 Unix domain socket)
- 接受連線:每當有新連線進來,
fork()出一個新的 Backend Process - 管理 Shared Memory:啟動時初始化並掛載共享記憶體段
- 監控子進程:定期檢查子進程狀態,若有異常採取行動
- 管理信號:接收 SIGHUP 重新載入設定、SIGTERM/SIGINT 啟動 shutdown
系統啟動時的進程樹:
init/systemd
└── postmaster(監聽 port 5432)
├── postgres: checkpointer
├── postgres: background writer
├── postgres: walwriter
├── postgres: autovacuum launcher
├── postgres: logical replication launcher
├── postgres: stats collector(PG14 以前)
├── postgres: backend (client: app@db) ← 每個連線
├── postgres: backend (client: app@db)
└── postgres: autovacuum worker ← 由 launcher 啟動
Postmaster 的崩潰處理策略
Backend Process 崩潰(SIGSEGV)時:
→ Postmaster 偵測到(wait())
→ 向所有其他 Backend 發送 SIGTERM(要求正常退出)
→ 等待所有進程退出
→ 重新啟動所有 Background Workers
→ 執行 crash recovery(重放 WAL)
→ 接受新連線
這就是為什麼 kill -9 一個 Backend 可能導致整個 cluster 重啟。PG16+ 改進了此行為:若崩潰的 Backend 未持有任何 shared lock 或 WAL insert lock,Postmaster 可以不重啟整個 cluster。
背景進程完整架構
PostgreSQL 背景進程架構:
┌───────────────────────────────────────────────────────┐
│ Postmaster │
│ (監聽 port 5432) │
└───────┬───────────────────────────────────────────────┘
│ fork()
│
┌─────┴────────────────────────────────────────────────┐
│ │
│ 資料持久性進程組 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ WAL Writer │ │ Checkpointer │ │
│ │ WAL buffer→磁碟 │ │ 定期建立 checkpoint│ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 緩衝區管理進程組 │
│ ┌─────────────────┐ │
│ │ Background Writer│ │
│ │ dirty buffer→磁碟│ │
│ └─────────────────┘ │
│ │
│ 自動維護進程組 │
│ ┌──────────────────┐ ┌───────────────────────┐ │
│ │ Autovacuum │→│ Autovacuum Worker × N │ │
│ │ Launcher │ │ (VACUUM + ANALYZE) │ │
│ └──────────────────┘ └───────────────────────┘ │
│ │
│ 複寫進程組 │
│ ┌──────────────────┐ ┌───────────────────────┐ │
│ │ WAL Sender │ │ WAL Receiver │ │
│ │ (Primary 側) │ │ (Standby 側) │ │
│ └──────────────────┘ └───────────────────────┘ │
└──────────────────────────────────────────────────────┘
Background Writer(bgwriter)
職責:定期將 Shared Buffers 中的 dirty buffers(已修改但尚未寫回磁碟的 page)寫出到磁碟,減輕 Checkpoint 時的 I/O 集中衝擊。
工作原理:
- 每隔
bgwriter_delay(預設 200ms)掃描一輪 - 每輪最多寫出
bgwriter_lru_maxpages(預設 100)個 dirty pages - 優先寫出最近最少使用(LRU)的 dirty pages
沒有 bgwriter 的情況:
INSERT 大量資料 → Shared Buffers 全滿
→ Backend 必須先將 dirty page 寫磁碟 → 延遲加劇
有 bgwriter 的情況:
bgwriter 持續在背景清理 dirty pages
→ Shared Buffers 總有乾淨的可替換頁
→ Backend 直接替換,無需等待磁碟寫入
-- 監控 bgwriter 效率
SELECT
buffers_clean, -- bgwriter 主動清理的 buffers
maxwritten_clean, -- bgwriter 因超過上限而停止的次數
buffers_backend, -- Backend 自己被迫清理的 buffers(應盡量低)
buffers_backend_fsync -- Backend 執行 fsync 的次數(應為 0)
FROM pg_stat_bgwriter;
-- 健康評估:
-- buffers_backend 應遠小於 buffers_clean
-- maxwritten_clean > 0 → 增加 bgwriter_lru_maxpages
-- buffers_backend_fsync > 0 → 嚴重!立即調查 I/O 瓶頸
WAL Writer
職責:定期將 WAL buffers 中的內容 flush 到磁碟 WAL 檔案。
- 每隔
wal_writer_delay(預設 200ms)執行一次 flush wal_writer_flush_after:WAL buffer 積累超過此量(預設 1MB)立即 flush
與 Backend 的分工:不是所有 WAL 寫入都由 WAL Writer 完成。COMMIT 時(若 synchronous_commit=on),Backend 自己觸發 WAL flush。WAL Writer 的主要貢獻是降低高頻小交易下的 I/O 次數(batching 效果)。
Checkpointer
職責:定期執行 Checkpoint——確保 Shared Buffers 中所有 dirty pages 都寫回磁碟。Checkpoint 是 Crash Recovery 的「起點」。
Checkpoint 執行流程:
1. 在 WAL 中插入 CHECKPOINT_BEGIN 記錄
2. 掃描 Shared Buffers
3. 將所有 dirty buffers 寫出到磁碟(分批,避免 I/O 峰值)
4. 等待所有寫出操作完成(fsync)
5. 在 WAL 中插入 CHECKPOINT_END 記錄
6. 更新 pg_control 檔案
兩種 Checkpoint 類型
| 類型 | 觸發條件 | 特性 |
|---|---|---|
| 定時(timed) | 每隔 checkpoint_timeout(預設 5 分鐘) | 正常維護,分批執行 |
| 要求式(required) | WAL 超過 max_wal_size(預設 1GB) | 更緊急,可能造成 I/O 峰值 |
-- 監控 Checkpoint 頻率
SELECT
checkpoints_timed, -- 定時觸發的 checkpoint 數
checkpoints_req, -- WAL 大小觸發的 checkpoint 數
checkpoint_write_time, -- 寫入磁碟的時間 (ms)
checkpoint_sync_time -- fsync 的時間 (ms)
FROM pg_stat_bgwriter;
-- checkpoints_req / (checkpoints_timed + checkpoints_req) 應 < 10%
-- 若過高,增加 max_wal_size 或 checkpoint_timeout
checkpoint_completion_target=0.9 的意義:Checkpoint 應該在下一個 Checkpoint 預期開始前的 90% 時間內完成,將 I/O 分散開來。
Autovacuum Launcher / Workers
Launcher 週期性喚醒(每 autovacuum_naptime,預設 1 分鐘),掃描 pg_stat_user_tables 找出需要 VACUUM 或 ANALYZE 的表,然後 fork Worker 處理。
同時最多 autovacuum_max_workers(預設 3)個 Worker 運行。
觸發條件
VACUUM 觸發:
dead tuple 數量 > autovacuum_vacuum_threshold(預設 50)
+ autovacuum_vacuum_scale_factor(預設 0.2)× 表的 row 數
例:100 萬 row 的表 → 50 + 0.2 × 1,000,000 = 200,050 dead tuples
ANALYZE 觸發:
修改的 tuple 數量 > autovacuum_analyze_threshold(預設 50)
+ autovacuum_analyze_scale_factor(預設 0.1)× 表的 row 數
大表的問題:1 億 row 的表需要 2000 萬個 dead tuple 才觸發 VACUUM,可能導致嚴重的表膨脹。
-- 對特定大表調低觸發條件
ALTER TABLE large_orders SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_analyze_scale_factor = 0.005,
autovacuum_vacuum_threshold = 1000
);
-- 查看 Autovacuum 目前正在處理哪些表
SELECT pid,
datname,
relid::regclass AS table_name,
phase,
heap_blks_scanned,
heap_blks_total,
ROUND(heap_blks_scanned::numeric / NULLIF(heap_blks_total, 0) * 100, 1) AS progress_pct
FROM pg_stat_progress_vacuum;
WAL Sender / WAL Receiver
WAL Sender 在 Primary 側運行,將 WAL stream 傳送給 Standby。WAL Receiver 在 Standby 側接收 WAL stream 並應用到本地。
-- 在 Primary 查看 WAL sender 狀態
SELECT pid, application_name, client_addr, state,
sent_lsn, write_lsn, flush_lsn, replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replication_lag_bytes
FROM pg_stat_replication;
-- 在 Standby 查看 WAL receiver 狀態
SELECT status, received_lsn,
last_msg_send_time, last_msg_receipt_time
FROM pg_stat_wal_receiver;
Stats Collector 架構演進
PG14 以前
Stats Collector 是獨立進程,透過 UDP 接收統計更新:
Backend → UDP socket → Stats Collector Process → 臨時檔案
↑
其他 backend 讀取
問題:stats 有延遲、進程重啟時可能遺失。
PG15+ 重大改變
完全移除 Stats Collector 進程,改用 Shared Memory 存放統計資訊:
Backend → Shared Memory(直接更新統計結構)
↑↓
所有 backend 直接讀取
Checkpoint 時持久化到磁碟
好處:統計資訊即時、啟動更快、不再有 stats 遺失問題。
Backend Process 生命週期
Client 發起連線
│
▼
Postmaster accept() TCP 連線
│ fork()
▼
Backend Process 啟動
1. 附掛 Shared Memory
2. 讀取 pg_hba.conf 驗證連線
3. 執行認證(密碼/GSSAPI/Kerberos 等)
4. 載入初始 session 設定
│
▼
進入查詢循環
┌─────────────────────────────┐
│ 接收 SQL │
│ → Parser(語法分析) │
│ → Analyzer(語意分析) │
│ → Rewriter(規則重寫) │
│ → Planner(生成執行計劃) │
│ → Executor(執行) │
│ → 回傳結果給 Client │
└─────────────────────────────┘
│(收到 Terminate 或 TCP 斷線)
▼
Backend Process 清理
1. Rollback 任何未提交交易
2. 釋放 lock
3. 分離 Shared Memory
4. 退出(exit())
監控 Backend 連線狀態
-- 查看所有連線的詳細狀態
SELECT pid, usename, datname, application_name,
client_addr, state,
wait_event_type, wait_event,
LEFT(query, 100) AS query_preview,
NOW() - query_start AS query_duration
FROM pg_stat_activity
WHERE backend_type = 'client backend'
ORDER BY query_start DESC NULLS LAST;
-- 找出 idle in transaction(佔住 lock 的危險連線)
SELECT pid, usename, state,
NOW() - state_change AS idle_duration, query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND state_change < NOW() - INTERVAL '5 minutes';
終止連線
-- 正常取消一個查詢(不斷開連線)
SELECT pg_cancel_backend(12345);
-- 強制斷開一個連線(SIGTERM)
SELECT pg_terminate_backend(12345);
-- 批量終止 idle in transaction 超過 10 分鐘的連線
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
AND state_change < NOW() - INTERVAL '10 minutes';
Background Workers API(PG9.3+)
PostgreSQL 提供 Background Workers API,允許 Extension 以背景進程形式運行:
| Extension | 使用 Background Worker |
|---|---|
| pg_cron | 定時執行 SQL 任務 |
| pglogical | 邏輯複寫 |
| TimescaleDB | 自動壓縮、連續聚合更新 |
| pg_partman | 自動分區管理 |
-- 查看已註冊的 background workers
SELECT pid, backend_type, state
FROM pg_stat_activity
WHERE backend_type NOT IN ('client backend', 'autovacuum worker');
進程間通訊機制
通訊機制概覽:
┌──────────────────────────────────────────────┐
│ Shared Memory │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ LWLock │ │ ProcArray│ │WAL Buffer│ │
│ │ (輕量鎖) │ │(進程狀態)│ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────┘
Signal(OS 信號):
SIGHUP → 重新載入設定(pg_hba.conf, postgresql.conf)
SIGTERM → 要求正常退出(smart shutdown)
SIGINT → pg_cancel_backend(取消查詢)
XID Wraparound 監控
-- 查看距離 XID wraparound 還有多遠(最高優先級!)
SELECT datname,
age(datfrozenxid) AS xid_age,
2147483648 - age(datfrozenxid) AS xids_until_wraparound
FROM pg_database
ORDER BY xid_age DESC;
-- xid_age 接近 2 億時要警戒
-- PostgreSQL 會停止接受新寫入直到 VACUUM 完成
-- 建議在 1.5 億時發出警報
常見陷阱
kill -9 導致 cluster 重啟
# 安全的方式:讓 PostgreSQL 自己處理
psql -c "SELECT pg_terminate_backend(12345);"
# 或用 SIGTERM
kill -SIGTERM 12345
# 危險:直接 kill -9 可能觸發 cluster restart
kill -9 12345 # 不建議!
autovacuum_max_workers 不足
預設只有 3 個 Worker,若同時有多個大表需要 VACUUM,會造成積壓。
-- 查看是否有 VACUUM 積壓
SELECT schemaname || '.' || relname AS table_name,
n_dead_tup,
last_autovacuum,
NOW() - last_autovacuum AS since_last_vacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 10000
AND (last_autovacuum IS NULL
OR last_autovacuum < NOW() - INTERVAL '1 hour')
ORDER BY n_dead_tup DESC;
-- 若有積壓,增加 worker 數(需重啟)
-- postgresql.conf: autovacuum_max_workers = 6
Standby 上的進程模型
Standby 的進程樹:
postmaster(monitoring for local connections)
├── postgres: startup ← 執行 streaming apply
├── postgres: walreceiver ← 從 Primary 接收 WAL
├── postgres: checkpointer
├── postgres: background writer
└── postgres: backend (read-only) × N
Standby 上的查詢是 read-only(Hot Standby),WAL Receiver 不斷從 Primary 接收 WAL 並交給 startup 進程 apply。
總結
背景進程 是 PostgreSQL 穩定運行的幕後功臣:
- Postmaster 是萬物之起點,負責連線管理與子進程監控
- Background Writer 持續清理 dirty buffers,避免 Backend 被迫同步寫磁碟
- WAL Writer 批量 flush WAL,降低高頻小交易的 I/O 次數
- Checkpointer 建立 Crash Recovery 的起點,透過
checkpoint_completion_target分散 I/O - Autovacuum 自動維護表的健康,大表需調低
scale_factor避免膨脹 - PG15 移除 Stats Collector 進程,改用 Shared Memory 直接存取統計資訊
- 監控
buffers_backend_fsync(應為 0)和 XID age(應 < 1.5 億)是生產環境的關鍵指標
下一篇,我們將進入 SQL 與資料操作系列,從 資料型別——PostgreSQL 豐富的型別系統開始。