CodeGym /課程 /SQL SELF /查詢執行計劃的基本概念: costrows

查詢執行計劃的基本概念: costrowswidth

SQL SELF
等級 41 , 課堂 1
開放

查詢執行計劃的基本概念:costrowswidth

當你寫一個 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 Cost0.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_age index 找到符合條件的列,再從表裡撈出來。
  • cost 明顯降低了:Startup Cost 變成 4.37Total Cost 變成 20.50
  • 因為有 index,操作效率提升很多。

視覺化:Seq Scan 跟 Index Scan 的差別

這裡有個小表格,讓你一眼看懂:

操作 介紹 範例
Seq Scan 整個表都讀一遍 每一列都檢查
Index Scan 用 index 來查 透過 index 快速找到列

陷阱跟常見錯誤

在用執行計劃參數時,要有心理準備會遇到一些意外。比如說,cost 低不一定代表查詢真的比較快。如果資料庫統計資料過時(像是大量更新表之後),計劃可能就不準。記得用 ANALYZE 指令更新統計資料。這個我們下堂課會再細講。

記得該用 index 的時候就用,但也別亂加一堆 index:index 會佔空間,也會拖慢寫入操作。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION