查詢最佳化工具使用資料庫的資料統計資訊來選擇具有最小總代價的查詢計劃,查詢代價通過磁碟I/O取得的磁碟頁面數作為單位來度量。 可以使用EXPLAIN和EXPLAIN ANALYZE語句發現和改進查詢計劃。
閱讀EXPLAIN輸出
查詢計劃類似於一棵有節點的樹,執行和閱讀的順序是自底而上。計劃中的每個節點表示一個操作,例如表掃描、表串連、聚集或者排序。閱讀的順序是從底向上:每個節點會把結果輸出給直接在它上面的節點。一個計劃中的底層節點通常是表掃描操作:順序掃描表、通過索引或者位元影像索引掃描表等。如果該查詢要求那些行上的串連、聚集、排序或者其他動作,就會有額外的節點在掃描節點上面負責執行這些操作。最頂層的計劃節點通常是資料庫的移動(MOTION)節點:重分布(REDISTRIBUTE)、廣播(BROADCAST)或者收集(GATHER)節點。這些操作在查詢處理時在執行個體節點之間移動資料。
EXPLAIN的輸出對於查詢計劃中的每個節點都顯示為一行並顯示該節點類型和下面的執行的代價估計:
- cost:以磁碟頁面擷取為單位度量。1.0等於一次順序磁碟頁面讀取。第一個估計是得到第一行的啟動代價,第二個估計是得到所有行的總代價。
- rows:這個計劃節點輸出的總行數。這個數字根據條件的過濾因子會小於被該計劃節點處理或者掃描的行數。最頂層節點的是估算的返回、更新或者刪除的行數。
- width:這個計劃節點輸出的所有行的總位元組數。
需要注意以下兩點:
- 一個節點的代價包括其子節點的代價。最頂層計劃節點有對於該計劃估計的總執行代價。這是最佳化器估算出來的最小的數字。
- 代價只反映了在資料庫中執行的時間,並沒有計算在資料庫執行之外的時間,例如將結果行傳送到用戶端花費的時間。
EXPLAIN樣本
下面的例子描述了如何閱讀一個查詢的EXPLAIN查詢代價:
EXPLAIN SELECT * FROM names WHERE name = 'Joelle';
QUERY PLAN
------------------------------------------------------------
Gather Motion 4:1 (slice1) (cost=0.00..20.88 rows=1 width=13)
-> Seq Scan on 'names' (cost=0.00..20.88 rows=1 width=13)
Filter: name::text ~~ 'Joelle'::text查詢最佳化工具會順序掃描names表,對每一行檢查WHERE語句中的filter條件,只輸出滿足該條件的行。 掃描操作的結果被傳遞給一個Gather Motion操作。Gather Motion是Segment把所有行發送給Master節點。在這個例子中,有4個Segment節點會並存執行,並向Master節點發送資料。這個計劃估計的啟動代價是00.00(沒有代價)而總代價是20.88次磁碟頁面擷取。最佳化器估計這個查詢將返回一行資料。
EXPLAIN ANALYZE
EXPLAIN ANALYZE除了顯示執行計畫還會運行語句。EXPLAIN ANALYZE計劃會把實際執行代價和最佳化器的估計一起顯示,同時顯示額外的下列資訊:
- 查詢執行的總已耗用時間(以毫秒為單位)。
- 查詢計劃每個Slice使用的記憶體,以及為整個查詢語句保留的記憶體。
- 計劃節點操作中涉及的Segment節點數量,其中只會統計返回行的Segment。
- 操作產生最多行的Segment節點返回的行最大數量。如果多個Segment節點產生了相等的行數,EXPLAIN ANALYZE會顯示那個用了最長結束時間的Segment節點。
- 為一個操作產生最多行的Segment節點的ID。
- 相關操作使用的記憶體量(work_mem)。如果work_mem不足以在記憶體中執行該操作,計劃會顯示溢出到磁碟的資料量最少的Segment的溢出資料量。例如:
Work_mem used: 64K bytes avg, 64K bytes max (seg0). Work_mem wanted: 90K bytes avg, 90K byes max (seg0) to lessen workfile I/O affecting 2 workers. - 產生最多行的Segment節點檢索到第一行的時間(以毫秒為單位)以及該Segment節點檢索到所有行花掉的時間。
EXPLAIN ANALYZE SELECT * FROM names WHERE name = 'Joelle';
QUERY PLAN
------------------------------------------------------------
Gather Motion 2:1 (slice1; segments: 2) (cost=0.00..20.88 rows=1 width=13)
Rows out: 1 rows at destination with 0.305 ms to first row, 0.537 ms to end, start offset by 0.289 ms.
-> Seq Scan on names (cost=0.00..20.88 rows=1 width=13)
Rows out: Avg 1 rows x 2 workers. Max 1 rows (seg0) with 0.255 ms to first row, 0.486 ms to end, start offset by 0.968 ms.
Filter: name = 'Joelle'::text
Slice statistics:
(slice0) Executor memory: 135K bytes.
(slice1) Executor memory: 151K bytes avg x 2 workers, 151K bytes max (seg0).
Statement statistics: Memory used: 128000K bytes Total runtime: 22.548 ms運行這個查詢花掉的總時間是22.548毫秒。Sequential scan操作只有Segment(seg0)節點返回了1行,用了0.255毫秒找到第一行且用了0.486毫秒來掃描所有的行。Segment向Master發送資料的Gather Motion接收到1行。這個操作的總消耗時間是0.537毫秒。常見查詢運算元
表掃描運算元
- 表掃描操作運算元(SCAN)掃描表中的行以尋找一個行的集合,包括:
- Seq Scan :順序掃描表中的所有行。
- Append-only Scan : 掃描行存追加最佳化表。
- Append-only Columnar Scan :掃描列存追加最佳化表中的行。
- Index Scan :遍曆一個B樹索引以從表中取得行。
- Bitmap Append-only Row-oriented Scan :從索引中收集僅追加表中行的指標並且按照磁碟上的位置進行排序。
- Dynamic Table Scan — 使用一個分區選擇函數來選擇分區。Function Scan節點包含分區選擇函數的名稱,可以是下列之一:
- gp_partition_expansion :選擇表中的所有分區。
- gp_partition_selection :基於一個等值運算式選擇一個分區。
- gp_partition_inversion : 基於一個範圍運算式選擇分區。
- 表掃描操作運算元(SCAN)掃描表中的行以尋找一個行的集合,包括:
表串連
- 表串連操作運算元(JOIN)包括:
- Hash Join :從較小的表構建一個雜湊表,用串連列作為雜湊鍵掃描較大的表,為串連列計算雜湊鍵並尋找具有相同雜湊鍵的行。雜湊串連通常是資料庫中最快的串連。計劃中的Hash Cond標識要被串連的列。
- Nested Loop Join :選擇在較大的表作為外表,迭代掃描較小的表中的行。Nested Loop Join要求廣播其中的一個小表,這樣一個表中的所有行才能與其他表中的所有行進行串連操作。Nested Loop Join在較小的表或者通過使用索引約束的表上執行得不錯,但在使用Nested Loop串連大型表時可能會有效能影響。設定配置參數enable_nestloop為OFF(預設)能夠讓最佳化器更偏向選擇Hash Join。
- Merge Join :排序兩個表並且將它們串連起來。Merge Join對於預排序好的資料很快。為了使查詢最佳化工具偏向選擇Merge Join,可將參數enable_mergejoin設定為ON。
- 表串連操作運算元(JOIN)包括:
移動
- 移動操作運算元(MOTION)在Segment節點之間移動資料,包括:
- Broadcast motion :每一個Segment節點將自己的行發送給所有其他Segment節點,這樣每一個Segment節點都有表的一份完整的本地拷貝。最佳化器通常只為小型表選擇Broadcast motion。對大型表來說,Broadcast motion是會比較慢的。在串連操作沒有按照串連鍵分布的情況下,可能會將把一個表中所需的行動態重分布到別的Segment節點上。
- Redistribute motion : 每一個Segment節點重新雜湊資料並且把行發送到對應於雜湊鍵的Segment節點上。
- Gather motion :來自所有Segment的結果資料被合并在一起發送到節點上(通常是Master節點)。對大部分查詢計劃來說這是最後的操作。
- 移動操作運算元(MOTION)在Segment節點之間移動資料,包括:
其他
- 查詢計劃中出現的其他動作運算元包括:
- Materialize :最佳化器將一個子查詢結果進行物化。
- InitPlan :預查詢,被用在動態分區消除中,當執行時還不知道最佳化器需要用來標識要掃描分區的值時,會執行這個預查詢。
- Sort : 為另操作(例如Aggregation或者Merge Join)進行所有資料排序。
- Group By : 通過一個或者更多列對行進行分組。
- Group/Hash Aggregate : 使用雜湊對行進行聚集操作。
- Append :串接資料集,例如在整合從分區表中各分區掃描的行時會用到。
- Filter :使用來自於一個WHERE子句的條件選擇行。
- Limit : 限制返回的行數。
- 查詢計劃中出現的其他動作運算元包括:
查詢最佳化工具的選擇
您可以通過查看EXPLAIN輸出來判斷計劃是由ORCA還是傳統查詢最佳化工具產生。這一資訊出現在EXPLAIN輸出的末尾。Settings行顯示配置參數OPTIMIZER的設定。Optimizer status行顯示該解釋計劃是由ORCA還是傳統查詢最佳化工具產生。
QUERY PLAN
------------------------------------------------------------------------------------
Aggregate (cost=0.00..296.14 rows=1 width=8)
-> Gather Motion 2:1 (slice1; segments: 2) (cost=0.00..295.10 rows=1 width=8)
-> Aggregate (cost=0.00..294.10 rows=1 width=8)
-> Table Scan on part (cost=0.00..97.69 rows=100040 width=1)
Settings: optimizer=on
Optimizer status: PQO version 1.609
(5 rows)
explain select count(*) from part; QUERY PLAN
----------------------------------------------------------------------------------------
Aggregate (cost=3519.05..3519.06 rows=1 width=8)
-> Gather Motion 2:1 (slice1; segments: 2) (cost=3518.99..3519.03 rows=1 width=8)
-> Aggregate (cost=3518.99..3519.00 rows=1 width=8)
-> Seq Scan on part (cost=0.00..3018.79 rows=100040 width=1)
Settings: optimizer=off
Optimizer status: legacy query optimizer
(5 rows)