查詢執行計劃的基本概念:cost、rows、width
當你寫一個 SQL 查詢時,PostgreSQL 不會馬上就執行它。它會先啟動自己的「大腦」——查詢優化器,產生一個執行計劃。這個計劃就像地圖上的路線一樣:PostgreSQL 會計算要做哪些動作、順序怎麼排,才能順利拿到資料。
查詢優化器會評估你查詢所有可能的執行路徑:像是順序掃描整個表、用 index、做 filter 跟 sort 等等。它會想辦法找出資源消耗最少的方式來執行你的查詢。也就是說,它會在執行時間和伺服器資源之間找個平衡點。
執行計劃的關鍵參數
好啦,現在來到重頭戲——拆解 PostgreSQL 在你執行 EXPLAIN 指令時給你的那些參數。先來個簡單例子:
EXPLAIN
SELECT * FROM students WHERE age > 20;
你會拿到類似這樣的結果:
Seq Scan on students (cost=0.00..35.00 rows=7 width=72)
Filter: (age > 20)
來拆一下這些看起來很神祕的詞跟數字。
1. cost(執行成本)
cost——這是 PostgreSQL 預估執行查詢會花掉多少資源。這個參數有兩個部分:
- Startup Cost:開始執行這個操作的成本(像是準備 index 之類的)。
- Total Cost:整個操作跑完的總成本。
舉個例:
cost=0.00..35.00
0.00——這是 Startup Cost。35.00——這是 Total Cost。
cost 越低,PostgreSQL 越喜歡這個計劃。但要記住,cost 是個相對值,不是秒數或毫秒,而是 PostgreSQL 內部的評分標準。
2. rows(預估資料列數)
rows 代表 PostgreSQL 預期這個階段會回傳或處理多少列。在我們的例子裡:
rows=7
意思是 PostgreSQL 預估 age > 20 這個 filter 會回傳 7 列。這個數字是根據 PostgreSQL 收集的統計資料來算的。如果統計資料過時,這個預測就會不準,可能導致 suboptimal 的計劃。
3. width(每列寬度,單位:byte)
width——這是這個階段每一列平均佔用的 byte 數。在我們的例子裡:
width=72
意思是每一列平均佔 72 byte。width 會考慮欄位的資料大小,還有像 row id 或系統資訊這些額外開銷。
這有點像你下載 app。如果它「很重」(類似 width 很大),就算你網速很快(類似 cost 很低),還是要花比較多時間下載。
執行計劃解析範例
來看個實際例子。假設我們有個 students 表:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
age INTEGER,
major VARCHAR(50)
);
然後我們執行這個查詢:
EXPLAIN
SELECT * FROM students WHERE age > 20 AND major = 'CS';
結果可能長這樣:
Seq Scan on students (cost=0.00..42.50 rows=3 width=164)
Filter: ((age > 20) AND (major = 'CS'))
- Seq Scan:PostgreSQL 用的是順序掃描
students表,也就是一列一列慢慢看過去。 - cost=0.00..42.50:這個操作的成本。
Startup Cost是0.00,總成本是42.50。 - rows=3:PostgreSQL 預估
age > 20 AND major = 'CS'這個 filter 會回傳 3 列。 - width=164:每一列平均佔 164 byte。
現在你就知道 PostgreSQL 怎麼做決策,也能找出查詢的瓶頸。比如說,如果你看到 cost 很高,可能代表查詢太重;如果 rows 很多,或許該檢查一下 filter。
cost 實戰怎麼運作?
來給 age 欄位加個 index:
CREATE INDEX idx_age ON students(age);
然後再跑一次查詢:
EXPLAIN
SELECT * FROM students WHERE age > 20 AND major = 'CS';
結果可能變成這樣:
Bitmap Heap Scan on students (cost=4.37..20.50 rows=3 width=164)
Recheck Cond: (age > 20)
Filter: (major = 'CS')
-> Bitmap Index Scan on idx_age (cost=0.00..4.37 rows=20 width=0)
Index Cond: (age > 20)
有什麼變化?
- 原本的
Seq Scan換成Bitmap Heap Scan:PostgreSQL 會先用idx_ageindex 找到符合條件的列,再從表裡撈出來。 cost明顯降低了:Startup Cost變成4.37,Total Cost變成20.50。- 因為有 index,操作效率提升很多。
視覺化:Seq Scan 跟 Index Scan 的差別
這裡有個小表格,讓你一眼看懂:
| 操作 | 介紹 | 範例 |
|---|---|---|
| Seq Scan | 整個表都讀一遍 | 每一列都檢查 |
| Index Scan | 用 index 來查 | 透過 index 快速找到列 |
陷阱跟常見錯誤
在用執行計劃參數時,要有心理準備會遇到一些意外。比如說,cost 低不一定代表查詢真的比較快。如果資料庫統計資料過時(像是大量更新表之後),計劃可能就不準。記得用 ANALYZE 指令更新統計資料。這個我們下堂課會再細講。
記得該用 index 的時候就用,但也別亂加一堆 index:index 會佔空間,也會拖慢寫入操作。
GO TO FULL VERSION