十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

華為雲開發者社群發表於2021-01-13
摘要:路徑生成是表關聯方式確定的主要階段,本文介紹了幾個影響路徑生成的要素:cost_param, scan方式,join方式,stream方式,並從原理上分析如何幹預路徑的生成。

一、cost模型選擇

顧名思義,cost_param是控制cost相關的一個引數。在瞭解cost_param之前,先回顧一下選擇率的概念,GaussDB優化器中的選擇率是指,當一個表有一個過濾或關聯條件時,通過該條件能被選中的行數佔總行數的比例,是介於0~1之間的一個實數。選擇率在優化器中是一個重要的概念,主要應用於行數和distinct值的估算,行數和distinct值是計劃生成中的基本要素。

首先,我們來看帶有過濾條件的基錶行數如何估算。如果一個表只有一個過濾條件,那麼以選擇率乘以表的行數,即可得到過濾完的行數;如果有多個過濾條件,那麼就需要算出一個綜合的選擇率,如何計算?方式有二:一是通過多列統計資訊直接計算,二是通過組合單列的選擇率。那麼組合的方式就由引數cost_param決定了,具體地,

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

舉一個例子,TPC-H 1x的part表,過濾條件是:p_brand = 'Brand#45' and p_container = 'WRAP CASE',檢視不同cost_param下的過濾後行數。

(1)cost_param=0

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

(2)cost_param=2

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

從估算出的行數(E-rows)和實際的行數(A-rows)對比可以看出,cost_param=0的不相關模型適合part表的p_brand和p_container列。

其次,Join的行數怎麼估算的呢?原理跟過濾條件的行數估算是類似的,如果沒有多列統計資訊可以使用,則也需要單獨計算每個條件的選擇率,然後計算出綜合選擇率,得出行數。例如 TPC-H 1x lineitem和orders關聯,關聯條件是:l_orderkey = o_orderkey and o_custkey = l_suppkey,不同cost_param的執行情況如下:

(1)cost_param=0

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

(2)cost_param=2

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

此例中,Join的列之間也適合完全相關模型,這與l_orderkey和l_suppkey的分佈是吻合的。

由於TPC-H的模型接近完全不相關模型,因此cost_param=0模型可以較好的描述場景,實際應用中,使用者可以根據具體業務場景來調整模型,行數估算的準確性是計劃生成的重要保證,在調優中檢查估算的最直接的地方。GaussDB會在後續版本中新增更多的模型供業務需求選擇。

二、Scan方式的選擇

GaussDB中掃描方式主要分順序掃描和索引掃描,每種掃描方式都對應若干掃描運算元,順序掃描在行列存中對應的掃描運算元分別是Seq Scan和CStore Scan運算元(下面我們討論中不加區分)。這些掃描運算元大部分都可以通過開關來進行調控,例如Seq Scan,如果設定enable_seqscan=off,則表示不會優先選擇Seq Scan,而不是一定不會選。掃描方式的選擇,很大程度上決定了獲取基表資料的路徑。我們以如下的例子來說明:

select l_orderkey, o_custkey from lineitem, orders where l_orderkey = o_orderkey;

lineitem分佈鍵是l_orderkey,並且在l_orderkey上有index,orders分佈鍵是o_orderkey。預設情況下,Scan的方式如下:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

兩個表都是順序掃描的路徑,關聯方式選擇了Hash Join。如果把Seq Scan關掉(enable_seqscan=off),計劃如下:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

lineitem的掃描變成了Index Only Scan(因為l_orderkey的型別是int),而在orders表上仍然選擇Seq Scan(因為沒有其他路徑),同時關聯方式也變為了Nest Loop,因為Hash Join需要全表掃描資料(lineitem的Seq Scan已經被關掉了)。優化器的選擇方式我們從代價(E-costs)一欄中也可以看出。再把Index Only Scan關掉,看看計劃如何變化:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

掃描路徑都變為了Seq Scan,而且Seq Scan的代價都很大。此時既然都走了Seq Scan,為什麼不選Hash Join呢,把Nest Loop關掉,看看Hash Join計劃的代價:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

從代價上看出Hash Join的總代價比Nest Loop的小,但優化器沒有選擇Hash Join,這是因為優化器比較路徑代價時,會比較Startup和Total代價,即啟動代價和總代價,綜合考慮,E-costs欄中顯示的是總代價。把explain_perf_mode設定為normal,檢視原Nest Loop的啟動代價:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

紅框中的兩個cost,分別是啟動代價和總代價,在看Hash Join的cost,明顯Hash Join的啟動代價比Nest Loop的大很多(啟動代價代表了輸出第一條資料的代價),優化器在比較路徑時,綜合了這兩個代價,最終推薦了Nest Loop的路徑。

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

從上面的例子可以看出,掃描路徑的調控,可以改變路徑生成,合理的搭配是生成最優計劃的前提,預設情況下,GaussDB優化器可以根據現有的路徑選擇(如上面的lineitem有兩條掃描路徑,orders只有一條掃描路徑),最後確定出最優的一條。兩條路徑代價比較時,總代價不是唯一要素,但總代價越小,一般也會越容易被選中。

三、關聯方式的選擇

GaussDB優化器中表關聯的主要方式有:Nest Loop,Hash Join和Merge Join,分別可以通過enable_nestloop、enable_hashjoin、enable_mergejoin進行控制,這種控制也不是絕對的,可以理解為是否優先選擇。大部分場景下,三種路徑的代價關係:Hash Join < Merge Join < Nest Loop。我們以一個簡單的關聯示例說明,store_returns和store_sales是TPC-DS 1x中兩個表,SQL如下:

select count(*) from store_returns, store_sales where sr_customer_sk = ss_customer_sk;

預設情況下,優化器推薦Hash Join路徑,計劃如下:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

如果把Hash Join關掉,則優化器選擇了Merge Join路徑:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

如果再把Merge Join路徑關掉,可能就會選擇Nest Loop路徑。關聯方式的控制開關一般用於調優或規避問題,但具體是否能夠起作用要看具體的語句,除了當前關聯方式,還有沒有其他方式。實際場景中,一個語句中關聯的運算元較多,一般很難用引數enable_hashjoin或enable_nestloop或enable_mergejoin來控制某兩個表的Join方式,GaussDB中更細緻的語句級別的調優手段是Plan Hint,感興趣的讀者可以參考產品手冊。

四、Stream方式的選擇

Stream運算元是GaussDB分散式執行的關鍵運算元之一,主要起到網路傳輸的作用,概要介紹可以參考:GaussDB(DWS)效能調優系列實戰篇一:十八般武藝之總體調優策略。Stream運算元由引數enable_stream_operator控制,如果關掉Stream運算元,則可能導致生成不下推的計劃,例如:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

因為lineitem表關聯的鍵l_partkey不是lineitem的分佈鍵,需要新增Stream運算元,但Stream功能被禁,於是只能生成不下推計劃。

GaussDB計劃中常見的主要Stream運算元包括Redistribute、Broadcast和Gather。Gather一般是分散式計劃中,CN用於收集DN的資料進行最後的處理,除非最後收集的行數非常多,這個運算元涉及效能問題一般較少。Redistribute和Broadcast一是對“互補”的運算元,前者用於重分佈,後者用於廣播,生成計劃時,優化器會根據代價大小來選擇。當Join Key沒有包含表的分佈鍵的時候,一般會新增Redistribute路徑,能選擇Redistribute路徑理論上也可選擇Broadcast路徑,最終選擇哪條路徑要看優化器估算的代價是多少。這兩個運算元可以通過引數enable_redistribute和enable_broadcast進行控制。

在SMP開啟的情況下,當並行度(dop)大於1時,一般還會有Local Redistribute、Split Redistribute、Local Broadcast和Split Broadcast;當傾斜優化開啟時,還有PART REDISTRIBUTE PART ROUNDROBIN、PART_REDISTRIBUTE_PART_BROADCAST、PART_REDISTERIBUTE_PART_LOCAL等等,這些也是Stream運算元,主要就是重分佈、廣播、RoundRobin的一些擴充套件形式,這裡我們不一一介紹了,感興趣的讀者可以參考GaussDB DWS 產品手冊。

我們考慮兩個表的簡單關聯,store_sales和sr_tbl,它們的分佈鍵分別是ss_item_sk 和sr_returned_date_sk,Join 條件是store_sales.ss_customer_sk =sr_tbl. sr_customer_sk,執行結果如下:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

由於兩個表的分佈鍵都不是Join Key,因此走Hash Join路徑的話需要有一個表做Broadcast或者兩個表都做Redistribute,但是store_sales表比較大(E-rows顯示28.7億行),而sr_tbl錶行數估算比較少(E-rows顯示100行),優化器認為適合做Broadcast。於是最終選擇了一邊Broadcast的計劃。

對於這個計劃,由於sr_tbl表統計資訊不準確(如果是中間結果集,則表示中間結果集估算不準),一種調優的方法是,將sr_tbl的表統計資訊重新收集準確一些(如果sr_tbl是中間結果集,則無法收集),另一種方法是讓sr_tbl走Redistribute路徑,而後者我們又有兩種方式來實現,一是用Plan Hint,即在生成計劃時,告訴優化器走Redistribute路徑,二是把Broadcast關掉。禁用Broadcast後,執行計劃如下:

十八般武藝玩轉GaussDB(DWS)效能調優:路徑干預

本列中,開啟了SMP自適應,即優化器會根據系統資源和當前Active SQL數量來自行決定並行度(dop),如果Redistribute和Broadcast選擇不當,則可能導致

(1)Broadcast計劃會出現下盤

(2)兩個計劃的並行度不一樣,最終執行時間可能會差異比較大。

對於Stream方式的控制,一般的調優方式有Plan Hint、GUC引數、改善統計資訊或估算資訊。

五、結束語

本文介紹的cost_param屬於cost底層引數,建議對資料特徵和使用場景比較熟悉的DBA慎重使用。Scan、Join、Stream調控的基本依據也是代價,代價一般體現在執行耗時上,調優時可從Performance中識別出效能的瓶頸點,分析選擇的運算元是否與代價匹配。另外,除了本文介紹的Session級別的控制引數外,還有基表、中間結果的行數,也可以通過Plan Hint進行語句級別的調控,感興趣讀者可通過GaussDB DWS產品文件進一步瞭解。

本文分享自華為雲社群《GaussDB(DWS)效能調優系列實戰篇五:十八般武藝之路徑干預》,原文作者:- 大道至簡 - 。

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章