EXPLAIN 命令用于顯示 PostgreSQL 執(zhí)行查詢的計劃。它提供了查詢的執(zhí)行步驟、每個步驟的執(zhí)行順序以及預(yù)計的資源消耗。這有助于識別和優(yōu)化性能瓶頸。EXPLAIN 可以與 ANALYZE 結(jié)合使用,以顯示實際執(zhí)行時的詳細統(tǒng)計信息。
EXPLAIN [ANALYZE] [VERBOSE] query;
-
ANALYZE:實際執(zhí)行查詢并顯示實際的運行時間和行數(shù)。 -
VERBOSE:顯示更詳細的信息。
EXPLAIN SELECT * FROM orders WHERE customer_id = 123 AND order_date > '2023-01-01';
Seq Scan on orders (cost=0.00..431.00 rows=21 width=58)
Filter: ((customer_id = 123) AND (order_date > '2023-01-01'::date))
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 123 AND order_date > '2023-01-01';
Seq Scan on orders (cost=0.00..431.00 rows=21 width=58) (actual time=0.016..0.021 rows=1 loops=1)
Filter: ((customer_id = 123) AND (order_date > '2023-01-01'::date))
Rows Removed by Filter: 5
Planning Time: 0.150 ms
Execution Time: 0.035 ms
輸出說明
EXPLAIN SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;
QUERY PLAN
------------------------------------------------------------------------------------------
Hash Join (cost=230.47..713.98 rows=101 width=488)
Hash Cond: (t2.unique2 = t1.unique2)
-> Seq Scan on tenk2 t2 (cost=0.00..445.00 rows=10000 width=244)
-> Hash (cost=229.20..229.20 rows=101 width=244)
-> Bitmap Heap Scan on tenk1 t1 (cost=5.07..229.20 rows=101 width=244)
Recheck Cond: (unique1 < 100)
-> Bitmap Index Scan on tenk1_unique1 (cost=0.00..5.04 rows=101 width=0)
Index Cond: (unique1 < 100)
https://www.postgresql.org/docs/current/using-explain.html
輸出一個查詢計劃的樹結(jié)構(gòu),最底層的是掃面結(jié)點,子節(jié)點先執(zhí)行,然后執(zhí)行上層節(jié)點,最上層的節(jié)點是語句的總體解釋。每個節(jié)點使用 -> 表示其層級關(guān)系和執(zhí)行順序
-
Seq Scan on orders:表示對
orders表進行順序掃描。 -
cost=0.00..431.00:表示預(yù)計的啟動成本(
0.00)和總成本(431.00)。- 啟動成本:從開始執(zhí)行到輸出第一行的成本。(比如排序,或者過濾的消耗)
- 總成本:處理完所有行的總成本。
-
rows=21:估計的返回行數(shù)。
返回的行數(shù)是預(yù)估的,所以不一定準確, 數(shù)據(jù)庫會對數(shù)據(jù)進行采樣預(yù)估 - width=58:每行的字節(jié)數(shù)。
- Filter:應(yīng)用的過濾條件。
-
actual time=0.016..0.021:實際開始時間(
0.016ms)和結(jié)束時間(0.021ms)。 - rows=1:實際返回的行數(shù)。
- loops=1:執(zhí)行的循環(huán)次數(shù)。
- Rows Removed by Filter: 5:被過濾條件排除的行數(shù)。
- Planning Time:查詢計劃生成所需時間。
- Execution Time:實際查詢執(zhí)行所需時間。
常見的節(jié)點操作類型
掃描
Seq Scan:逐行掃描整個表,檢查每一行是否符合查詢條件。
適用于小表或無索引的查詢。
全表掃描,可能會較慢,對于大表特別不高效。
Index Scan:使用索引掃描,適用于有索引的列。
適用于有索引的表。
更快的查詢速度,因為只掃描符合條件的部分數(shù)據(jù)。
會連接原來的表數(shù)據(jù)
對于范圍查詢,Index Scan會先查找范圍的起始值,然后順序讀取所有符合條件的條目。
Index Only Scan:僅使用索引數(shù)據(jù),無需訪問表數(shù)據(jù)。
Bitmap Heap Scan:結(jié)合位圖索引掃描和堆掃描。
Bitmap Index Scan:首先,數(shù)據(jù)庫系統(tǒng)使用位圖索引掃描(Bitmap Index Scan)來查找符合查詢條件的行的位圖。這些位圖是索引的“位圖表示”,標識了哪些索引項匹配查詢條件。
Bitmap Heap Scan:然后,數(shù)據(jù)庫系統(tǒng)使用生成的位圖來訪問堆表(即實際數(shù)據(jù)表),從中提取符合條件的行。
適用范圍:
范圍掃描:當查詢條件匹配的數(shù)據(jù)范圍較大,Bitmap Heap Scan 可以更高效地處理這種情況。(比起index scan 不用遍歷過去)
多條件查詢:當查詢涉及多個條件時,位圖索引掃描可以有效地結(jié)合多個索引的結(jié)果
連接
- Join Type
內(nèi)連接,左連接,右連接,全連接 - Inner Unique
內(nèi)表的連接鍵是否有唯一約束
Nested Loop Join:嵌套循環(huán)連接,用于小數(shù)據(jù)集。
對外部表的每一行執(zhí)行內(nèi)表的掃描,尋找匹配的行。
Hash Join:使用哈希表來執(zhí)行連接操作。
首先對一個表進行哈希,然后使用這個哈希表來查找另一個表中匹配的行。
適用于連接大表,特別是當連接鍵的選擇性較低時。
Merge Join:先對兩個表按連接鍵排序,然后合并匹配的行。
適用于已排序的表或可以快速排序的表。
對于連接鍵的選擇性較高時性能較好。
其他
Hash:計算哈希值
Aggregate 執(zhí)行聚合操作,例如 SUM(), COUNT(), AVG() 等。
Sort 對數(shù)據(jù)進行排序操作
Group 用于對數(shù)據(jù)進行分組,通常與聚合操作一起使用。
Limit 限制查詢結(jié)果的返回行數(shù)
Materialize 用于臨時存儲中間結(jié)果集,以便在查詢執(zhí)行過程中重復使用這些結(jié)果 ,比如CTE
- Parallel Aware :能否并行
- Parent Relationship: 和父節(jié)點的關(guān)系
- Outer:表示這個節(jié)點是作為其父節(jié)點的外部部分執(zhí)行的。例如,在連接操作中,這個節(jié)點可能是外部表的掃描操作。
- Inner:表示這個節(jié)點是作為其父節(jié)點的內(nèi)部部分執(zhí)行的。例如,在連接操作中,這個節(jié)點可能是內(nèi)表的掃描操作。
- Top:表示這是查詢計劃的頂層操作,通常是最終的結(jié)果集。
注意如果添加了 ANALYZE, 后面的語句會實際執(zhí)行,所以如果想要不影響數(shù)據(jù)庫的現(xiàn)有數(shù)據(jù),需要執(zhí)行
BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;
所以實際使用的時候,并不時一定會使用索引的,比如如果只是過濾一下主鍵,過濾出來的數(shù)據(jù)集還是很大,那么可能用順序掃描,如果指定主鍵是誰,那么會用index掃描
pgadmin里面可以直接用Explain按鈕,帶有圖形化界面,很方便
_serial :執(zhí)行順序,越大的越先執(zhí)行
statistics :有哪些節(jié)點類型,執(zhí)行了多少次,參與了哪些表,表里面什么掃描,掃描了多少次