CodeGym /課程 /SQL SELF /什麼是 EXPLAIN,以及怎麼用它來分析查詢

什麼是 EXPLAIN,以及怎麼用它來分析查詢

SQL SELF
等級 41 , 課堂 2
開放

想像一下,你正在開發一個應用程式,結果某個查詢突然變成資料庫裡最貴的那一個。你開始發現 app 會卡住或出現小當機。這時候 EXPLAIN 就是你的救星,能幫你搞清楚到底是哪裡出問題。根據 EXPLAIN 的分析來優化查詢,可以幫你省下資源、節省時間,還能讓使用者對你的 app 印象更好。

EXPLAIN 就像讓你能窺探 PostgreSQL 裡面到底怎麼執行查詢。它會告訴你會不會用到 index,還是會整張表都掃過一遍(full table scan),optimizer 會做哪些步驟、順序怎麼排,還有中間結果大概多大。

換句話說,EXPLAIN 讓你知道查詢執行時會發生什麼事:這個查詢「重不重」、大概會處理幾筆資料、會用到哪些資源。當查詢突然變慢時,這就是你找出原因的神兵利器。

EXPLAIN 就像黑暗中的手電筒:有了它你就能看到底下發生什麼事,還有哪裡出包了。

EXPLAIN 指令語法

來看看 EXPLAIN 的基本語法:

EXPLAIN 你的_SQL_查詢;

查詢範例:

EXPLAIN SELECT * FROM students WHERE age > 20;

當你執行這個指令時,PostgreSQL 不會真的執行查詢。它只會給你一份執行計畫。就像蓋房子前先看設計圖一樣——先知道會做什麼,才不會一不小心搞砸。

這是個輸出範例:

Seq Scan on students  (cost=0.00..35.00 rows=7 width=37)
  Filter: (age > 20)

這個輸出看起來可能有點嚇人,不過別擔心——我們馬上來拆解它的重點。

基本執行計畫解析

我們來拆解一下這個結果:

  1. Seq Scan on students —— 這代表 PostgreSQL 會把 students 這張表整個掃過一遍(sequential scan)。這不一定是壞事,但如果表很大,Seq Scan 可能會很慢。

  2. cost=0.00..35.00 —— 這是這個操作的成本估算:

    • Startup Cost:這個操作一開始的成本(這裡是 0.00)。
    • Total Cost:整個操作跑完的總成本(這裡是 35.00)。
  3. rows=7 —— PostgreSQL 預估 age > 20 這個條件會回傳 7 筆資料。這叫「基數」(cardinality)。如果你看到很奇怪的預估值,可能是你的表統計資料過時了。

  4. width=37 —— 這是一筆資料的平均大小(byte)。

  5. Filter: (age > 20) —— 這表示 PostgreSQL 會對每一筆資料都檢查這個條件。

所以 EXPLAIN 的輸出讓你知道 PostgreSQL 的策略和假設。你可以用這些資訊來優化查詢。

EXPLAIN 指令選項

雖然 EXPLAIN 的基本輸出已經很有用,你還可以用下面這些選項讓它更強大:

ANALYZE

加上這個選項,PostgreSQL 不只會給你執行計畫,還會真的執行查詢,然後給你實際的數據。範例:

EXPLAIN ANALYZE SELECT * FROM students WHERE age > 20;

這樣你就能比較 PostgreSQL 的預估和實際執行結果,看看準不準。

VERBOSE

會顯示更多細節,適合想深入分析的時候用。範例:

EXPLAIN VERBOSE SELECT * FROM students WHERE age > 20;

BUFFERS

會顯示查詢執行時用到的記憶體 buffer。要跟 ANALYZE 一起用:

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM students WHERE age > 20;

COSTS

如果你想隱藏或顯示成本資訊(cost),可以用這個選項:

EXPLAIN (COSTS OFF) SELECT * FROM students WHERE age > 20;

FORMAT

執行計畫可以用不同格式輸出,比如 JSON 或 XML。範例:

EXPLAIN (FORMAT JSON) SELECT * FROM students WHERE age > 20;

EXPLAIN 使用範例

假設你有一個 university 資料庫,裡面有 students 這張表。你想找出所有年齡大於 20 歲的學生:

EXPLAIN SELECT * FROM students WHERE age > 20;

可能會得到這樣的輸出:

Seq Scan on students  (cost=0.00..35.00 rows=7 width=37)
  Filter: (age > 20)

就像前面說的,這是 sequential scan(Seq Scan),對大表來說可能不太有效率。

現在我們來對 age 欄位建立一個 index,看看計畫會不會變:

CREATE INDEX age_index ON students(age);

EXPLAIN SELECT * FROM students WHERE age > 20;

輸出:

Index Scan using age_index on students  (cost=0.15..4.23 rows=7 width=37)
  Index Cond: (age > 20)

這時 PostgreSQL 就會用 index scan(Index Scan),通常對大表來說會快很多。

常見問題與錯誤

為什麼我的查詢有 index 還是很慢?

可能是查詢回傳的資料太多,這樣用 index 反而沒什麼好處。也有可能 index 品質不好或沒更新。

如果 EXPLAIN 的輸出看不懂怎麼辦?

先從簡單的查詢開始,一次看一個執行計畫的節點。

怎麼知道表的統計資料是不是過時了?

執行 ANALYZE students

什麼時候要用沒有 ANALYZEEXPLAIN

如果你只想看計畫,不想真的執行查詢(像是會改資料的查詢)。

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