CodeGym /課程 /SQL SELF /執行計劃解讀:閱讀與分析節點(`Seq Scan`、`Index Scan`、`Hash Join`)

執行計劃解讀:閱讀與分析節點(`Seq Scan`、`Index Scan`、`Hash Join`)

SQL SELF
等級 41 , 課堂 4
開放

今天我們要搞懂 PostgreSQL 執行計劃裡的節點是什麼、怎麼看,還有最重要的——怎麼判斷哪裡怪怪的。你會知道為什麼資料庫有時候硬要用超吃資源的 Seq Scan,明明你都建好 index 了,甚至不只一個。

當 PostgreSQL 在建立查詢執行計劃時,它會把查詢拆成一個個步驟,這些步驟就叫做節點。每個節點就像資料庫伺服器處理你查詢時的一個「步驟」。主要的節點類型有:

Sequential Scan(Seq Scan

Seq Scan,也就是「順序掃描」,是最簡單的從資料表撈資料的方式。PostgreSQL 會直接把整個表一行一行讀過去,然後檢查每一行有沒有符合你的查詢條件。

什麼時候會用 Seq Scan
Seq Scan 會在這些情況下出現:

  • 資料表沒有適合的 index 可以加速查詢。
  • 過濾條件太寬泛,index 幫不上忙(像是要撈超過 50% 的資料)。
  • PostgreSQL 覺得直接順序讀表會比用 index 還快(有時候小表真的會這樣)。
EXPLAIN SELECT * FROM students WHERE age > 18;

結果範例:

Seq Scan on students  (cost=0.00..35.50 rows=10 width=50)
  Filter: (age > 18)

注意 Seq Scan on students —— PostgreSQL 在這裡就是說它會整張 "students" 表都讀一遍。

Seq Scan 的問題: 如果表很大,順序掃描會超級慢。

Index Scan

Index Scan 就是用 index 來掃資料。你在 PostgreSQL 建 index,其實就像幫資料表做個「目錄」。如果查詢能用到 index,PostgreSQL 就只會去找需要的那幾行,不用整張表都翻。

什麼時候會用 Index Scan

  • 查詢有針對有 index 的欄位加過濾條件(像 WHERE)。
  • 有用到比較運算,像 =<>BETWEEN 等等。
CREATE INDEX idx_students_age ON students(age);

EXPLAIN SELECT * FROM students WHERE age = 18;

結果範例:

Index Scan using idx_students_age on students  (cost=0.15..8.27 rows=1 width=50)
  Index Cond: (age = 18)

這裡 Index Scan using idx_students_age 就是 PostgreSQL 告訴你它有用 idx_students_age 這個 index。原本要一行一行翻的,現在直接用 index 快速定位。

Index Scan 的優點:

  • 大表查詢速度大幅提升。
  • 減少從硬碟讀資料的量。

Index Scan 的問題:
如果查詢要撈太多資料(像超過一半的表),用 index 反而可能比 Seq Scan 還慢。

Hash Join

Hash Join 是用來把兩張表根據 join 條件(像 ON students.course_id = courses.id)合在一起的。PostgreSQL 會先幫其中一張(通常比較小)建一個 hash table,然後用這個 hash table 去另一張表找對應的資料。

什麼時候會用 Hash Join

  • INNER JOINLEFT JOIN 等等把表 join 起來時。
  • PostgreSQL 覺得 Hash Join 比其他 join 方法更有效率時。
EXPLAIN
SELECT * 
FROM students 
JOIN courses ON students.course_id = courses.id;

結果範例:

Hash Join  (cost=25.00..50.00 rows=10 width=100)
  Hash Cond: (students.course_id = courses.id)
  -> Seq Scan on students  (cost=0.00..20.00 rows=10 width=50)
  -> Hash  (cost=15.00..15.00 rows=10 width=50)
       -> Seq Scan on courses  (cost=0.00..15.00 rows=10 width=50)

這裡 Hash Join 就是把兩張表 join 起來。注意 PostgreSQL 會先對兩張表都做 Seq Scan,然後再建 hash table(Hash)。

Hash Join 的優點:

  • 中型表 join 起來很快。
  • 適合 join 很多資料的情境。

Hash Join 的問題:
如果 hash table 太大超過記憶體,PostgreSQL 會把它寫到硬碟,這樣 join 就會變超慢。

執行計劃分析範例

來看一個實際的例子。

查詢:

EXPLAIN ANALYZE
SELECT *
FROM students
JOIN courses ON students.course_id = courses.id
WHERE students.age > 18;

結果:

Hash Join  (cost=35.00..75.00 rows=5 width=100) (actual time=1.00..2.50 rows=5 loops=1)
  Hash Cond: (students.course_id = courses.id)
  -> Seq Scan on students  (cost=0.00..40.00 rows=10 width=50) (actual time=0.50..1.00 rows=7 loops=1)
        Filter: (age > 18)
        Rows Removed by Filter: 3
  -> Hash  (cost=25.00..25.00 rows=5 width=50) (actual time=0.30..0.30 rows=5 loops=1)
       -> Seq Scan on courses  (cost=0.00..20.00 rows=5 width=50) (actual time=0.20..0.25 rows=5 loops=1)
Planning Time: 0.50 ms
Execution Time: 3.00 ms

解讀:

  1. Hash Join:主節點。PostgreSQL 把 studentscourses 兩張表 join 起來。
    • actual time:1.00 到 2.50 毫秒。
    • rows=5:查詢回傳 5 筆資料。
  2. 巢狀節點:
    • Seq Scan on students:順序讀 students 表,並套用 (age > 18) 過濾。
    • Rows Removed by Filter = 3:有 3 行被過濾掉。
    • Hash:PostgreSQL 幫 courses 表建 hash table。

節點比較與選擇

在分析執行計劃時,重點是要懂 PostgreSQL 為什麼選這種資料處理方式。有時候你得自己動手優化,比如加 index 或重寫查詢。給你幾個小建議:

  • 如果看到大表用 Seq Scan,考慮加 index。
  • Hash Join 太慢時,檢查 PostgreSQL 的記憶體設定。
  • EXPLAIN ANALYZE 比較預估和實際的指標(rowstime)。

到這裡你已經有基本概念,知道怎麼看查詢執行計劃和解讀節點了。接下來的課我們會聊常見的優化問題和解法。

1
問卷/小測驗
查詢執行計劃,等級 41,課堂 4
未開放
查詢執行計劃
查詢執行計劃
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION