Hint作為一種SQL補充文法,允許使用者通過相應的文法改變SQL的執行方式,實現SQL的特殊最佳化。Hologres從V2.2版本開始,提供Hint文法,以便您使用Hint改變SQL的執行方式,輔助業務進行SQL調優,實現更好的效能。本文為您介紹Hint的用法以及使用情境。
前提條件
使用HINT功能前,請確保您已根據業務需求在Session層級或DB層級設定如下GUC參數:
Session層級開啟。
SET pg_hint_plan_enable_hint=on;DB層級開啟,建立串連後生效。
ALTER DATABASE <dbname> SET pg_hint_plan_enable_hint=on;
使用限制
Hint功能要求Hologres執行個體版本必須為V2.2及以上版本,若您的執行個體為V2.1或以下版本,請升級執行個體。
使用說明
目前支援對常規表(包括外部表格)、子查詢和CTE(Common Table Expression)指定Hint,不支援對視圖指定Hint。
Hint的所有內容均包含在
/*+HINT和*/之間,Hint中不允許再出現注釋。Hint關鍵詞不區分大小寫。
一段Hint內容中可以同時包含多個Hint關鍵詞。
Hint有對應的層級,指定的參數為當前範圍內的可見參數,不可使用子查詢或父查詢中的參數,如下述SQL中的Hint
/*+HINT Leading(tt t2) */只能指定tt和t2為參數,無法使用t1、t3、t。同理,/*+HINT Leading(t t1) */的位置只能使用t和t1,無法使用t2、t3、tt。SELECT /*+HINT Leading(t t1) */ * FROM t1 join ( SELECT /*+HINT Leading(tt t2) */ * FROM t2 join ( SELECT * FROM t3 ) tt ) t;對於
INSERT INTO ... SELECT語句,INSERT的範圍包含目標表和後面SELECT最外層的源表,SELECT的範圍不包含目標表。且為了避免衝突情況,當在INSERT後指定了對應的Hint時,SELECT中不能再次指定Hint。樣本如下:正確樣本
--樣本1:INSERT後的Hint支援指定target、t1、t2為參數。 INSERT /*+HINT Leading(target (t1 t2)) */ INTO target SELECT t1.a,t2.b FROM t1 JOIN t2 ON t1.a=t2.a --樣本2:SELECT後的Hint只支援指定t1、t2為參數。 INSERT INTO target SELECT /*+HINT Leading(t2 t1) */ t1.a,t2.b FROM t1 JOIN t2 ON t1.a=t2.a;錯誤樣本
--不能在INSERT和SELECT後同時使用Hint,否則執行會報錯。 INSERT /*+HINT Leading(target (t1 t2)) */ INTO target SELECT /*+HINT Leading(t2 t1) */ t1.a,t2.b FROM t1 JOIN t2 ON t1.a=t2.a; --報錯資訊 ERROR: insert statement with hint should not have sub select with hint at the same time
GUC Hint僅對當前Query層級生效,且在任意層級下設定都會影響整個Query。當前Query執行完成後,後續Query將不會受GUC Hint的影響。
樣本:在子查詢中設定GUC關閉
count distinct reuse會對整個Query都生效。SELECT count(DISTINCT a), count(DISTINCT b) FROM ( SELECT /*+HINT set(hg_experimental_enable_reuse_cte_of_count_distinct off) */ t1.a t2.b FROM t1 JOIN t2 ON t1.a = t2.a)Hint允許在參數中使用括弧以提高優先順序,允許任意的括弧嵌套。例如
Leading(t1 t2 t3)表示t1和t2先進行Join,再Join t3,而Leading(t1 (t2 t3))表示先執行t2 Join t3,再和t1進行Join。Join Method Hint和Join Order Hint均要求至少兩個以上的有效參數,參數不足時,Hint不會生效。
說明有效參數:指當前層級範圍中包含的表、子查詢和CTE,如
Leading(t1 t1)和Leading(t1)中都只有t1一個有效參數。Runtime Filter Hint只能對HashJoin生效。
當產生的候選計劃不包含Join Method指定的表串連方式時,對應的Hint不會生效。例如指定HashJoin(t1 t2),但產生的計劃為
t1 Join t3後再Join t2,此時Hint不會生效,可以通過添加Leading(t1 t2)指定Join Order來強制執行串連順序。SELECT後連續多段由
/*HINT+和*/括起的內容,只有第一段會被作為Hint處理。例如SELECT /*+HINT HashJoin(t1 t2) */ /*+HINT Leading(t1 t2) */ ...語句中,只有HashJoin會被處理,而Leading的內容會被忽略。同一類Hint中定義的表存在衝突時,以先定義的Hint為準。
說明衝突包含如下幾種情境:
兩個Hint中包含相同的表。
表集合相同。
Join Order時,Hint參數互為子集。
Join Method、Runtime Filter或skew Join時,Hint參數不互為子集。
樣本1:
HashJoin(t1 t2)和NestLoop(t2 t1)中包含相同的表,產生衝突,只解析HashJoin(t1 t2)。SELECT /*+HINT HashJoin(t1 t2) NestLoop(t2 t1) */ ...樣本2:
Leading(t1 t2)和Leading(t1 t2 t3)互為子集,產生衝突,因此只解析Leading(t1 t2)。SELECT /*+HINT Leading(t1 t2) Leading(t1 t2 t3) */ ...
當指定的Hint計劃不滿足產生條件時,會導致無法產生執行計畫。例如指定兩表進行NestLoop和Right Join時,由於不支援這樣的計劃,會報錯
ERROR: ORCA failed to produce a plan : No plan has been computed for required properties,即屬性不滿足無法產生計劃。
書寫格式
使用如下格式書寫Hint,然後執行對應的SQL即可:
SELECT|UPDATE|INSERT|DELETE /*+HINT <HintName(params)> */ ...HintName(params)表示Hint關鍵詞及對應參數,詳情請參見Hint關鍵詞。
Hint關鍵詞不區分大小寫。
僅允許直接在INSERT、UPDATE、DELETE和SELECT關鍵字後指定Hint。
Hint內容應位於
/*+HINT和*/之間。
Hint關鍵詞
目前各類型支援的Hint關鍵詞及對應的參數格式如下。
類型 | 參數格式 | 描述 | 樣本 | 注意事項 |
Join Method |
| 強制使用嵌套迴圈串連。 | |
|
| 強制使用HashJoin串連。 | | ||
Join Order |
| 強制Join串連按指定順序進行。 | | |
| 強制定義Join串連的順序和方向。 說明 Join pair是一對用括弧括起來的表或其他串連對,可以形成嵌套結構。 | | ||
Runtime Filter |
| 強制對指定表上的HashJoin使用Runtime Filter。 | |
|
GUC |
| 在構造計劃時,指定GUC參數值。 說明
| |
|
Motion Hint |
| 強制指定JOIN中,表集合的一側進行Broadcast。 | |
|
| 強制指定JOIN中,表集合的一側不進行Broadcast。 | | ||
| 強制指定JOIN中,表集合的一側進行Gather。 | | ||
| 強制指定JOIN中,表集合的一側不進行Gather。 | |
使用情境
下述以具體樣本為您介紹Hint的使用情境。樣本表的DDL語句如下:
CREATE TABLE target (a int primary key, b int);
CREATE TABLE t1 (a int, b int);
CREATE TABLE t2 (a int, b int);
CREATE TABLE t3 (a int);
CREATE TABLE t4 (a int);使用Hint調整Join Order
Join Order主要用於調整表Join的順序,不合理的Join Order會對SQL查詢效能產生極大影響,而造成Join Order不合理的原因通常為統計資訊缺失或統計資訊不準確。
統計資訊缺失:通常是因為未及時執行Analyze操作導致,Analyze詳情請參見ANALYZE和AUTO ANALYZE。
統計資訊不準確:通常發生在進行了過濾或Join操作之後,統計資訊過時,導致實際結果集和預估行數發生較大的偏差。
當Join Order不合理時,您可以根據業務實際進行手動使用GUC或者Hint調整。相比GUC的方式,通過Hint調整Join Order會更加簡單方便。
如下SQL樣本中,Join指的是t1 Join t2,HashJoin需要使用小表構建雜湊表(即執行計畫中Hash運算元下方的部分),如果實際上t2表的行數遠大於t1,SQL查詢效能會降低。解決方案除更新統計資料(執行Analyze操作)之外,您還可以使用Hint調整Join Order。例如:使用Leading(t2 t1)將Join順序調整為t2 Join t1後,執行計畫更加合理,且執行效率更高。
SQL樣本
SELECT /*+HINT Leading(t2 t1) */ t1.a FROM t1 JOIN t2 ON t1.a = t2.a;執行計畫對比
未開啟Hint的執行計畫
QUERY PLAN ----------------------------------------------------------------------------------- Gather (cost=0.00..10.07 rows=1000 width=4) -> Hash Join (cost=0.00..10.05 rows=1000 width=4) Hash Cond: (t1.a = t2.a) -> Redistribution (cost=0.00..5.01 rows=1000 width=4) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=4) -> Hash (cost=5.01..5.01 rows=1000 width=4) -> Redistribution (cost=0.00..5.01 rows=1000 width=4) Hash Key: t2.a -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on t2 (cost=0.00..5.00 rows=1000 width=4)開啟Hint的執行計畫
QUERY PLAN ----------------------------------------------------------------------------------- Gather (cost=0.00..10.07 rows=1000 width=4) -> Hash Join (cost=0.00..10.05 rows=1000 width=4) Hash Cond: (t2.a = t1.a) -> Redistribution (cost=0.00..5.01 rows=1000 width=4) Hash Key: t2.a -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on t2 (cost=0.00..5.00 rows=1000 width=4) -> Hash (cost=5.01..5.01 rows=1000 width=4) -> Redistribution (cost=0.00..5.01 rows=1000 width=4) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=4)
使用GUC Hint
在某些情境中,Query需要使用GUC參數才能達到更好的效果。GUC Hint主要用於設定Query層級的GUC參數,類似於在Query執行前設定GUC參數。通過GUC Hint可以高效的對某個Query設定GUC,Query執行完成後,GUC參數即失效,以便降低對其他Query的影響。
SQL樣本
SELECT /*+HINT set(hg_experimental_query_batch_size 512) */t1.a FROM t1 JOIN t2 ON t1.a = t2.a;執行計畫
QUERY PLAN Hash Join (cost=0.00..10.00 rows=1 width=4) Hash Cond: (t1.a = t2.a) -> Gather (cost=0.00..5.00 rows=1 width=4) -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t1 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=5.00..5.00 rows=1 width=4) -> Gather (cost=0.00..5.00 rows=1 width=4) -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t2 (cost=0.00..5.00 rows=1 width=4)
CTE和子查詢的Hint使用
在包含CTE和子查詢的情境下,使用Hint影響其執行計畫。
SQL樣本
WITH c1 AS ( SELECT /*+HINT Leading(t2 t1) */ t1.a FROM ( ( SELECT /*+HINT leading(t2 t1) */ t1.a FROM t1 JOIN t2 ON t1.a = t2.a ) AS t1 JOIN ( SELECT /*+HINT NestLoop(t4 t3) */ t4.a FROM t3 JOIN t4 ON t3.a = t4.a ) AS t2 ON t1.a = t2.a ) ), c2 AS ( SELECT /*+HINT leading(t1 t2) */ t2.a FROM ( ( SELECT /*+HINT Leading(t1 t2) */ t1.a FROM t1 JOIN t2 ON t1.a = t2.a ) AS t1 JOIN ( SELECT /*+HINT Leading(t4 t3) */ t4.a FROM t3 JOIN t4 ON t3.a = t4.a ) AS t2 ON t1.a = t2.a ) ) SELECT /*+HINT NestLoop(v2 v1) */ * FROM ( ( SELECT /*+HINT Leading (c1 t2) */ c1.a FROM c1 JOIN t2 ON c1.a = t2.a ) AS v1 JOIN ( SELECT /*+HINT Leading (t1 c2) */ c2.a FROM t1 JOIN c2 ON t1.a = c2.a ) AS v2 ON v1.a = v2.a ) ORDER BY v2.a;執行計畫
QUERY PLAN Sort (cost=0.00..10660048.36 rows=1 width=8) Sort Key: t4_1.a -> Gather (cost=0.00..10660048.36 rows=1 width=8) -> Nested Loop (cost=0.00..10660048.36 rows=1 width=8) Join Filter: ((t1.a = t4_1.a) AND (t1.a = t1_1.a) AND (t2_1.a = t1_1.a) AND (t2_1.a = t4_1.a)) -> Hash Join (cost=0.00..25.01 rows=1 width=8) Hash Cond: (t1_1.a = t4_1.a) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t1_1.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t1 t1_1 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=20.00..20.00 rows=1 width=4) -> Hash Join (cost=0.00..20.00 rows=1 width=4) Hash Cond: ((t1_2.a = t4_1.a) AND (t1_2.a = t3_1.a) AND (t2_2.a = t3_1.a) AND (t2_2.a = t4_1.a)) -> Hash Join (cost=0.00..10.00 rows=1 width=8) Hash Cond: (t1_2.a = t2_2.a) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t1_2.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t1 t1_2 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=5.00..5.00 rows=1 width=4) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t2_2.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t2 t2_2 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=10.00..10.00 rows=1 width=8) -> Hash Join (cost=0.00..10.00 rows=1 width=8) Hash Cond: (t4_1.a = t3_1.a) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t4_1.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t4 t4_1 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=5.00..5.00 rows=1 width=4) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t3_1.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t3 t3_1 (cost=0.00..5.00 rows=1 width=4) -> Materialize (cost=0.00..10385.07 rows=40 width=8) -> Broadcast (cost=0.00..10385.07 rows=40 width=8) -> Hash Join (cost=0.00..10385.07 rows=1 width=8) Hash Cond: (t1.a = t2_1.a) -> Hash Join (cost=0.00..10380.07 rows=1 width=4) Hash Cond: ((t4.a = t1.a) AND (t3.a = t1.a) AND (t3.a = t2.a) AND (t4.a = t2.a)) -> Redistribution (cost=0.00..10370.07 rows=1 width=8) Hash Key: t4.a -> Nested Loop (cost=0.00..10370.07 rows=1 width=8) Join Filter: (t3.a = t4.a) -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t3 (cost=0.00..5.00 rows=1 width=4) -> Materialize (cost=0.00..5.00 rows=40 width=4) -> Broadcast (cost=0.00..5.00 rows=40 width=4) -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t4 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=10.00..10.00 rows=1 width=8) -> Hash Join (cost=0.00..10.00 rows=1 width=8) Hash Cond: (t2.a = t1.a) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t2.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t2 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=5.00..5.00 rows=1 width=4) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t1 (cost=0.00..5.00 rows=1 width=4) -> Hash (cost=5.00..5.00 rows=1 width=4) -> Redistribution (cost=0.00..5.00 rows=1 width=4) Hash Key: t2_1.a -> Local Gather (cost=0.00..5.00 rows=1 width=4) -> Seq Scan on t2 t2_1 (cost=0.00..5.00 rows=1 width=4)
INSERT Hint的使用
通常當目標表與源表有關聯,需要進行Join Order或其他相關調整時,在INSERT INTO ... SELECT的情境中使用Hint文法。INSERT INTO ... SELECT的應用情境中,SQL邏輯會比較複雜,需要業務根據計劃來添加Hint。
樣本1:Hint作用於INSERT目標表和SELECT查詢最外層的源表。
執行過程中,如果
t1 Join t2產生的資料量較小,目標表target的資料量較大,當統計資訊未更新時,可能無法產生最優的執行計畫,可以通過Hint來調整Join Order,實現更好的效能。SQL樣本
--Hint作用在INSERT目標表和SELECT查詢最外層的源表 INSERT /*+HINT Leading(target (t1 t2)) */ INTO target SELECT t1.a,t2.b FROM t1 JOIN t2 ON t1.a=t2.a;執行計畫
QUERY PLAN ----------------------------------------------------------------------------------------------------------------- Gather (cost=0.00..26.57 rows=1000 width=8) -> Insert (cost=0.00..26.54 rows=1000 width=8) -> Project (cost=0.00..16.12 rows=1000 width=8) -> Hash Right Join (cost=0.00..15.12 rows=1000 width=12) Hash Cond: (target.a = t1.a) -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on target (cost=0.00..5.00 rows=1000 width=4) -> Hash (cost=10.07..10.07 rows=1000 width=8) -> Redistribution (cost=0.00..10.07 rows=1000 width=8) Hash Key: t1.a -> Hash Join (cost=0.00..10.06 rows=1000 width=8) Hash Cond: (t1.a = t2.a) -> Redistribution (cost=0.00..5.01 rows=1000 width=4) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=4) -> Hash (cost=5.01..5.01 rows=1000 width=8) -> Redistribution (cost=0.00..5.01 rows=1000 width=8) Hash Key: t2.a -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t2 (cost=0.00..5.00 rows=1000 width=8)
樣本2:Hint作用於SELECT子查詢。
SQL樣本
說明以下兩種INSERT語句使用Hint的情境是等效的。
INSERT INTO target SELECT /*+HINT Leading(t2 t1) */ t1.a,t2.b FROM t1 JOIN t2 ON t1.a=t2.a; INSERT /*+HINT Leading(t2 t1) */ INTO target SELECT t1.a,t2.b FROM t1 JOIN t2 ON t1.a=t2.a;執行計畫
QUERY PLAN ----------------------------------------------------------------------------------------------------------- Gather (cost=0.00..26.57 rows=1000 width=8) -> Insert (cost=0.00..26.54 rows=1000 width=8) -> Project (cost=0.00..16.12 rows=1000 width=8) -> Hash Left Join (cost=0.00..15.12 rows=1000 width=12) Hash Cond: (t1.a = target.a) -> Redistribution (cost=0.00..10.07 rows=1000 width=8) Hash Key: t1.a -> Hash Join (cost=0.00..10.06 rows=1000 width=8) Hash Cond: (t2.a = t1.a) -> Redistribution (cost=0.00..5.01 rows=1000 width=8) Hash Key: t2.a -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t2 (cost=0.00..5.00 rows=1000 width=8) -> Hash (cost=5.01..5.01 rows=1000 width=4) -> Redistribution (cost=0.00..5.01 rows=1000 width=4) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=4) -> Hash (cost=5.00..5.00 rows=1000 width=4) -> Local Gather (cost=0.00..5.00 rows=1000 width=4) -> Seq Scan on target (cost=0.00..5.00 rows=1000 width=4)
UPDATE HINT的使用
UPDATE語句中HINT通常也用於目標表和源表有關聯,需要進行手動調整的情況。
SQL樣本
t1表資料量大於target,設定HINT使target表作為雜湊表,達到調整Join Order的目的。
UPDATE /*+HINT Leading(t1 target) */ target SET b=t1.b+1 FROM t1 WHERE t1.a=target.a;執行計畫對比
未開啟Hint的執行計畫
QUERY PLAN ----------------------------------------------------------------------------------------------- Gather (cost=0.00..52.77 rows=1000 width=1) -> Update (cost=0.00..52.76 rows=1000 width=1) -> Project (cost=0.00..11.09 rows=1000 width=32) -> Hash Join (cost=0.00..10.08 rows=1000 width=32) Hash Cond: (target.a = t1.a) -> Local Gather (cost=0.00..5.00 rows=1000 width=28) -> Seq Scan on target (cost=0.00..5.00 rows=1000 width=28) -> Hash (cost=5.01..5.01 rows=1000 width=8) -> Redistribution (cost=0.00..5.01 rows=1000 width=8) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=8)開啟Hint的執行計畫
QUERY PLAN ---------------------------------------------------------------------------------------------- Gather (cost=0.00..52.77 rows=1000 width=1) -> Update (cost=0.00..52.76 rows=1000 width=1) -> Project (cost=0.00..11.09 rows=1000 width=32) -> Hash Join (cost=0.00..10.08 rows=1000 width=32) Hash Cond: (t1.a = target.a) -> Redistribution (cost=0.00..5.01 rows=1000 width=8) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=8) -> Hash (cost=5.00..5.00 rows=1000 width=28) -> Local Gather (cost=0.00..5.00 rows=1000 width=28) -> Seq Scan on target (cost=0.00..5.00 rows=1000 width=28)
Runtime Filter Hint的使用
Hologres支援Runtime Filter,當SQL不滿足Runtime Filter的產生條件時,可以使用Hint強制產生Runtime Filter,提升查詢效能。
只有在執行HashJoin操作時,才能使用Hint強制產生Runtime Filter。
並不是每次強制產生Runtime Filter後,都會提升查詢效能,需要根據業務情況綜合評估。
SQL樣本
SELECT /*+HINT runtimefilter(t1 t2) */ * FROM t1 JOIN t2 ON t1.a = t2.a;執行計畫
--plan中出現runtime filter,說明觸發了runtime filter QUERY PLAN ----------------------------------------------------------------------------------- Gather (cost=0.00..10.13 rows=1000 width=16) -> Hash Join (cost=0.00..10.07 rows=1000 width=16) Hash Cond: (t1.a = t2.a) Runtime Filter Cond: (t1.a = t2.a) -> Redistribution (cost=0.00..5.01 rows=1000 width=8) Hash Key: t1.a -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=8) Runtime Filter Target Expr: t1.a -> Hash (cost=5.01..5.01 rows=1000 width=8) -> Redistribution (cost=0.00..5.01 rows=1000 width=8) Hash Key: t2.a -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t2 (cost=0.00..5.00 rows=1000 width=8)
Motion HINT的使用
樣本1:強制t1表在JOIN時進行Broadcast,通常是在Stats不準確,導致資料Shuffle量非常大的時候使用。由於Hash JOIN目前只能在build端進行Broadcast,因此需要Leading指定JOIN ORDER,避免由於缺少Stats,Broadcast的cost懲罰值過大,導致選擇
t1 JOIN t2的順序。SQL樣本
SELECT /*+HINT Leading(t2 t1) Broadcast(t1) */ * FROM t1 JOIN t2 ON t1.a = t2.a;執行計畫
QUERY PLAN ---------------------------------------------------------------------------------------------- Gather (cost=0.00..100000000000000005366162204393472.00 rows=1000 width=16) -> Hash Join (cost=0.00..100000000000000005366162204393472.00 rows=1000 width=16) Hash Cond: (t2.a = t1.a) -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t2 (cost=0.00..5.00 rows=1000 width=8) -> Hash (cost=100000000000000005366162204393472.00..100000000000000005366162204393472.00 rows=3000 width=8) -> Broadcast (cost=0.00..100000000000000005366162204393472.00 rows=3000 width=8) -> Local Gather (cost=0.00..5.00 rows=1000 width=8) -> Seq Scan on t1 (cost=0.00..5.00 rows=1000 width=8)
樣本2:強制t1表不使用Broadcast。通常適用於採用了Broadcast的表統計資訊不準確,導致實際行數很大的表預估結果很少,從而在處理大表時進行Broadcast操作的效能很差的情境。
樣本資料
CREATE TABLE test1(a int, b int); CREATE TABLE test2(a int, b int); INSERT INTO test1 SELECT 1, i FROM generate_series(1, 10) AS i; INSERT INTO test2 SELECT 1,i FROM generate_series(1, 1000000) AS i; analyze test1,test2;SQL樣本
不使用HINT走的Broadcast
explain SELECT * FROM test1 JOIN test2 ON test1.b = test2.b;使用HINT禁止了Broadcast
explain SELECT /*+HINT NoBroadcast(test1) */ * FROM test1 JOIN test2 ON test1.b = test2.b;執行計畫
不使用HINT走的Broadcast
QUERY PLAN --------------------------------------------------------------------------------- Gather (cost=0.00..51.98 rows=1000000 width=16) -> Hash Join (cost=0.00..13.12 rows=1000000 width=16) Hash Cond: (test2.b = test1.b) -> Local Gather (cost=0.00..5.23 rows=1000000 width=8) -> Seq Scan on test2 (cost=0.00..5.20 rows=1000000 width=8) -> Hash (cost=5.00..5.00 rows=200 width=8) -> Broadcast (cost=0.00..5.00 rows=200 width=8) -> Local Gather (cost=0.00..5.00 rows=10 width=8) -> Seq Scan on test1 (cost=0.00..5.00 rows=10 width=8)使用HINT禁止了Broadcast
QUERY PLAN --------------------------------------------------------------------------------- Gather (cost=0.00..53.23 rows=1000000 width=16) -> Hash Join (cost=0.00..14.37 rows=1000000 width=16) Hash Cond: (test2.b = test1.b) -> Redistribution (cost=0.00..6.48 rows=1000000 width=8) Hash Key: test2.b -> Local Gather (cost=0.00..5.23 rows=1000000 width=8) -> Seq Scan on test2 (cost=0.00..5.20 rows=1000000 width=8) -> Hash (cost=5.00..5.00 rows=10 width=8) -> Redistribution (cost=0.00..5.00 rows=10 width=8) Hash Key: test1.b -> Local Gather (cost=0.00..5.00 rows=10 width=8) -> Seq Scan on test1 (cost=0.00..5.00 rows=10 width=8)