PostgreSQL 是一個功能強大的開源關聯式資料庫,但預設設定通常較為保守。本文將介紹如何在 Ubuntu 22.04 上對 PostgreSQL 進行效能最佳化,以充分發揮硬體資源的潛力。
效能調校概述
PostgreSQL 的預設設定是為了確保在各種硬體環境下都能穩定運行,因此通常設定得相當保守。針對特定的硬體配置和使用情境進行調校,可以顯著提升資料庫效能。
效能調校的主要面向包括:
- 記憶體配置:調整快取和工作記憶體
- 連線管理:優化連線池和最大連線數
- WAL 設定:平衡資料安全性和寫入效能
- 自動清理:調整 autovacuum 參數
- 查詢優化:使用 EXPLAIN 分析和索引優化
設定檔位置:
1
| /etc/postgresql/14/main/postgresql.conf
|
修改設定後需要重新載入或重啟服務:
1
2
3
4
5
| # 重新載入設定(大部分參數)
sudo systemctl reload postgresql
# 重新啟動服務(部分參數需要重啟)
sudo systemctl restart postgresql
|
shared_buffers 設定
shared_buffers 是 PostgreSQL 用於快取資料的共享記憶體區域,是最重要的效能參數之一。
建議設定
一般建議將 shared_buffers 設定為系統總記憶體的 25%:
1
2
| # 編輯設定檔
sudo nano /etc/postgresql/14/main/postgresql.conf
|
根據系統記憶體調整:
1
2
3
4
5
6
7
8
| # 8GB 記憶體系統
shared_buffers = 2GB
# 16GB 記憶體系統
shared_buffers = 4GB
# 32GB 記憶體系統
shared_buffers = 8GB
|
注意事項
- 設定過高可能導致作業系統記憶體不足
- 超過 8GB 後效益遞減,建議不超過總記憶體的 40%
- 修改此參數需要重啟 PostgreSQL 服務
查看目前設定
work_mem 設定
work_mem 定義了每個查詢操作(如排序、雜湊等)可使用的記憶體大小。
建議設定
1
2
3
4
5
6
7
8
| # 一般 OLTP 應用
work_mem = 64MB
# 複雜報表查詢
work_mem = 256MB
# 資料倉儲應用
work_mem = 512MB
|
計算公式
考慮最大連線數的影響:
1
| work_mem = (總記憶體 - shared_buffers) / (max_connections * 3)
|
注意事項
- 此值是每個操作的記憶體,非每個連線
- 複雜查詢可能同時使用多個 work_mem
- 可在 session 層級動態調整:
1
| SET work_mem = '256MB';
|
effective_cache_size 設定
effective_cache_size 是 PostgreSQL 估計作業系統檔案快取大小的參數,用於查詢規劃器決策。
建議設定
通常設定為系統總記憶體的 50%-75%:
1
2
3
4
5
| # 16GB 記憶體系統
effective_cache_size = 12GB
# 32GB 記憶體系統
effective_cache_size = 24GB
|
說明
- 此參數不會實際分配記憶體
- 影響查詢規劃器選擇索引掃描或序列掃描
- 設定過低可能導致查詢計劃不佳
連線管理設定
max_connections
最大連線數設定:
1
2
3
4
5
| # 一般 Web 應用
max_connections = 200
# 使用連線池時可降低
max_connections = 100
|
連線池建議
建議使用 PgBouncer 作為連線池:
1
| sudo apt install pgbouncer -y
|
基本設定 /etc/pgbouncer/pgbouncer.ini:
1
2
3
4
5
6
7
8
9
10
11
| [databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
|
WAL 設定優化
Write-Ahead Logging (WAL) 設定影響資料持久性和寫入效能。
wal_buffers
WAL 緩衝區大小:
1
2
3
4
5
| # 自動設定(推薦)
wal_buffers = -1
# 手動設定
wal_buffers = 64MB
|
checkpoint 設定
1
2
3
4
5
6
7
8
| # 檢查點之間的最大 WAL 大小
max_wal_size = 2GB
# 檢查點之間的最小 WAL 大小
min_wal_size = 1GB
# 檢查點完成目標(0.0-1.0)
checkpoint_completion_target = 0.9
|
synchronous_commit
控制事務提交的同步行為:
1
2
3
4
5
| # 預設值,最安全
synchronous_commit = on
# 效能優先,可能丟失少量資料
synchronous_commit = off
|
完整 WAL 設定範例
1
2
3
4
5
6
| wal_level = replica
wal_buffers = 64MB
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9
wal_compression = on
|
自動清理(autovacuum)調校
Autovacuum 負責回收已刪除資料佔用的空間並更新統計資訊。
基本設定
1
2
3
4
5
6
7
8
| # 啟用自動清理
autovacuum = on
# 同時執行的最大 autovacuum 程序數
autovacuum_max_workers = 3
# 檢查間隔(毫秒)
autovacuum_naptime = 60s
|
閾值設定
1
2
3
4
5
6
7
8
9
10
11
| # 觸發 vacuum 的最小行數變更
autovacuum_vacuum_threshold = 50
# 觸發 vacuum 的比例因子
autovacuum_vacuum_scale_factor = 0.1
# 觸發 analyze 的最小行數變更
autovacuum_analyze_threshold = 50
# 觸發 analyze 的比例因子
autovacuum_analyze_scale_factor = 0.05
|
大型資料表調校
對於大型資料表,可單獨設定較低的比例因子:
1
2
3
4
| ALTER TABLE large_table SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_analyze_scale_factor = 0.005
);
|
監控 autovacuum
1
2
3
4
5
6
7
8
9
10
11
| -- 查看 autovacuum 活動
SELECT schemaname, relname, last_vacuum, last_autovacuum,
vacuum_count, autovacuum_count
FROM pg_stat_user_tables;
-- 查看需要 vacuum 的資料表
SELECT schemaname, relname, n_dead_tup, n_live_tup,
round(n_dead_tup::numeric / nullif(n_live_tup, 0) * 100, 2) as dead_ratio
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
|
查詢效能分析(EXPLAIN)
使用 EXPLAIN 分析查詢執行計劃是效能調校的重要工具。
基本用法
1
2
3
4
5
6
7
8
9
| -- 顯示執行計劃
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- 顯示執行計劃和實際執行時間
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
-- 詳細輸出
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM users WHERE email = 'test@example.com';
|
輸出解讀
1
2
3
| Seq Scan on users (cost=0.00..1234.00 rows=1 width=100) (actual time=0.015..12.345 rows=1 loops=1)
Filter: (email = 'test@example.com'::text)
Rows Removed by Filter: 9999
|
關鍵指標:
- cost:估計成本(啟動成本..總成本)
- rows:估計返回行數
- actual time:實際執行時間(毫秒)
- Seq Scan:順序掃描(可能需要索引)
常見執行計劃類型
| 類型 | 說明 |
|---|
| Seq Scan | 全表掃描 |
| Index Scan | 索引掃描 |
| Index Only Scan | 僅索引掃描(最佳) |
| Bitmap Index Scan | 點陣圖索引掃描 |
| Nested Loop | 巢狀迴圈連接 |
| Hash Join | 雜湊連接 |
| Merge Join | 合併連接 |
索引優化
適當的索引可以大幅提升查詢效能。
建立索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| -- 基本 B-tree 索引
CREATE INDEX idx_users_email ON users(email);
-- 唯一索引
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
-- 複合索引
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- 部分索引
CREATE INDEX idx_orders_pending ON orders(status) WHERE status = 'pending';
-- GIN 索引(適用於陣列、JSONB)
CREATE INDEX idx_products_tags ON products USING GIN(tags);
|
查看索引使用情況
1
2
3
4
5
6
7
8
9
10
11
| -- 查看索引使用統計
SELECT schemaname, relname, indexrelname,
idx_scan, idx_tup_read, idx_tup_fetch
FROM pg_stat_user_indexes
ORDER BY idx_scan DESC;
-- 找出未使用的索引
SELECT schemaname, relname, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
AND indexrelname NOT LIKE '%pkey%';
|
索引維護
1
2
3
4
5
6
7
8
| -- 重建索引
REINDEX INDEX idx_users_email;
-- 重建表的所有索引
REINDEX TABLE users;
-- 並行重建(PostgreSQL 12+)
REINDEX INDEX CONCURRENTLY idx_users_email;
|
索引建議
- 為經常出現在 WHERE 子句的欄位建立索引
- 為外鍵欄位建立索引以加速 JOIN
- 考慮使用複合索引取代多個單欄位索引
- 定期檢查並移除未使用的索引
- 避免過度索引,每個索引都會增加寫入成本
完整設定範例
以下是 16GB 記憶體伺服器的建議設定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| # 記憶體設定
shared_buffers = 4GB
work_mem = 64MB
maintenance_work_mem = 1GB
effective_cache_size = 12GB
# 連線設定
max_connections = 200
# WAL 設定
wal_buffers = 64MB
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9
wal_compression = on
# 查詢規劃器
random_page_cost = 1.1
effective_io_concurrency = 200
# 日誌設定
log_min_duration_statement = 1000
log_checkpoints = on
log_lock_waits = on
# Autovacuum 設定
autovacuum = on
autovacuum_max_workers = 3
autovacuum_vacuum_scale_factor = 0.1
autovacuum_analyze_scale_factor = 0.05
|
效能監控
pg_stat_statements
啟用查詢統計擴充套件:
1
| CREATE EXTENSION pg_stat_statements;
|
查看最耗資源的查詢:
1
2
3
4
| SELECT query, calls, total_exec_time, mean_exec_time, rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
|
即時監控
1
2
3
4
5
6
7
8
9
| -- 查看目前連線
SELECT pid, usename, application_name, state, query
FROM pg_stat_activity
WHERE state != 'idle';
-- 查看鎖定狀況
SELECT pid, relation::regclass, mode, granted
FROM pg_locks
WHERE NOT granted;
|
參考資料