Spark_算子調(diào)優(yōu)
770
2025-04-01
1.?????? 技術(shù)背景
在SQL語句復(fù)雜、處理數(shù)據(jù)量大的AP場景下,單個查詢對內(nèi)存的需求越來越大,多個語句的并發(fā)很容易將系統(tǒng)的內(nèi)存吃滿,造成內(nèi)存不足的問題。為了應(yīng)對這種問題,GaussDB(DWS)引入了內(nèi)存自適應(yīng)控制的技術(shù),在上述場景下能夠?qū)\(yùn)行的作業(yè)進(jìn)行內(nèi)存級的管控,避免高并發(fā)場景下內(nèi)存不足產(chǎn)生的各種問題。
2.?????? GaussDB(DWS)的靜態(tài)內(nèi)存管理機(jī)制及缺陷
GaussDB(DWS)的執(zhí)行引擎繼承自PG,對于優(yōu)化器生成的執(zhí)行計劃樹,總體采取執(zhí)行算子+流水線的處理方式,如下圖所示。
對于NestLoop算子節(jié)點(diǎn),需要首先從左樹的IndexScan算子節(jié)點(diǎn)獲取元組,然后到右子樹的IndexScan算子節(jié)點(diǎn)進(jìn)行連接,匹配元組后進(jìn)行輸出。流水線的執(zhí)行方式使得對于NestLoop, IndexScan類的一般算子,同時只有一定數(shù)量的元組處于內(nèi)存中,對于行引擎每個算子僅占用一條元組的空間,對于列引擎占用一個batch(最多1000條元組)的空間,占用的空間較小,基本可以忽略不計。
但是,GaussDB(DWS)中也有一些需要將所有數(shù)據(jù)收集后進(jìn)行處理的算子,在執(zhí)行時需要使用較多的內(nèi)存,通常我們稱這類算子為物化算子。GaussDB(DWS)中主要存在如下不同種類的物化算子:
(1)?????? HashJoin:Hash連接操作符,主要思想是計算左右兩表連接列的hash值,通過hash值比較減少元組比較的次數(shù),需要將一個表建立hash表,另一個表進(jìn)行hash值比較操作,建立hash表需要在內(nèi)存中進(jìn)行。
(2)?????? HashAgg:Hash聚集操作符,主要思想同HashJoin類似,通過hash值比較減少元組去重比較的次數(shù),需要將不同值的元組保存的內(nèi)存中。
(3)?????? Sort:排序操作符,需要獲取所有元組后進(jìn)行排序操作,待排序元組均存在于內(nèi)存中。
(4)?????? Materialize:物化操作符,通常在需要重復(fù)掃描時使用,通過將結(jié)果存儲在內(nèi)存中,保證重復(fù)掃描時的效率。
同時,GaussDB也提供下盤的機(jī)制,當(dāng)上述操作符需要使用的內(nèi)存太大時,可以將部分或全部的數(shù)據(jù)下盤處理,提高內(nèi)存的使用效率,但相應(yīng)的查詢性能也會受到影響。PG使用 work_mem參數(shù)來控制算子可使用內(nèi)存的閾值,當(dāng)使用內(nèi)存超過閾值時,就需要做下盤處理。GaussDB(DWS)的靜態(tài)內(nèi)存管理機(jī)制也延續(xù)了PG的處理機(jī)制,使用work_mem來控制單算子的內(nèi)存使用上限。
GaussDB(DWS)的靜態(tài)內(nèi)存管理存在較大弊端,需要調(diào)優(yōu)人員能夠根據(jù)數(shù)據(jù)量、語句復(fù)雜程度和系統(tǒng)的內(nèi)存大小設(shè)置合理的work_mem,既避免work_mem設(shè)置太大導(dǎo)致系統(tǒng)資源不夠用,還要考慮到數(shù)據(jù)規(guī)模,保證大部分算子不下盤。通常情況下,這個是很難做到的,有以下幾點(diǎn)原因:
(1)?????? 通常情況下,復(fù)雜語句的執(zhí)行計劃中包含多個復(fù)雜算子,每個算子的內(nèi)存使用上限是work_mem,我們沒有辦法計算一個語句要使用多少內(nèi)存,因此也就不容易設(shè)置一個最優(yōu)的work_mem參數(shù),保證盡可能不下盤,同時內(nèi)存又夠用。并發(fā)場景更無法設(shè)置了。
(2)?????? work_mem只是每個算子內(nèi)存使用的上限,并不是預(yù)分配;如果數(shù)據(jù)量沒有那么大的話,實(shí)際內(nèi)存使用是達(dá)不到work_mem的。因此也會影響work_mem的設(shè)置。
(3)?????? 每個語句的場景不一樣,有的語句包含多個物化算子,而另外的語句只有一個物化算子,而這個算子對內(nèi)存的需求會比較大,因此無法全局統(tǒng)一地進(jìn)行設(shè)置。
3.?????? GaussDB(DWS)的內(nèi)存自適應(yīng)技術(shù)介紹
針對靜態(tài)內(nèi)存管理機(jī)制的弊端,我們設(shè)計了內(nèi)存自適應(yīng)控制技術(shù),目的有兩個:
(1)?????? 去除靜態(tài)內(nèi)存管理對work_mem的依賴。可以由SQL引擎優(yōu)化器模塊自動估算每個算子所需的內(nèi)存。
(2)?????? 避免大并發(fā)場景下內(nèi)存不足現(xiàn)象的發(fā)生。資源管理模塊根據(jù)SQL引擎優(yōu)化器對于每個查詢內(nèi)存的估算值,對每個查詢進(jìn)行調(diào)度,如果超過系統(tǒng)可用內(nèi)存,則進(jìn)行排隊(duì)。
如上圖所示,動態(tài)資源管理與內(nèi)存自適應(yīng)技術(shù)的組件圖如上圖所示。我們從多個CN中選擇一個CN,命名為CCN(Central CN),進(jìn)行語句隊(duì)列的管理。對于每個查詢SQL,CN在生成完執(zhí)行計劃后,為每個物化算子分配合適的內(nèi)存,同時計算整個語句內(nèi)存使用量,并將語句及對應(yīng)的內(nèi)存使用量發(fā)給CCN。CCN維護(hù)系統(tǒng)可用的內(nèi)存值,對于新來的語句,如果語句內(nèi)存使用量小于可用內(nèi)存值,則允許其下發(fā)到DN執(zhí)行,否則掛起,等到有語句結(jié)束釋放內(nèi)存后再次將其喚醒,是否可以下發(fā)。
為了達(dá)到上述目的,SQL引擎實(shí)現(xiàn)了內(nèi)存自適應(yīng)控制技術(shù),步驟如下:
(1)對于每個SQL,生成計劃前首先從資源管理模塊獲取系統(tǒng)當(dāng)前的最大可用內(nèi)存(Query Max Mem)和當(dāng)前可用內(nèi)存(System Available Mem)。最大可用內(nèi)存通常為每個DN的最大可用內(nèi)存去除系統(tǒng)預(yù)分配內(nèi)存,例如:數(shù)據(jù)緩存等,表示語句可用的最大內(nèi)存,如果語句使用內(nèi)存超過該值,必須下盤。當(dāng)前可用內(nèi)存用于表示當(dāng)前系統(tǒng)的繁忙程度,如果當(dāng)前可用內(nèi)存比較小,傾向于選擇耗費(fèi)內(nèi)存少的計劃。
(2)依據(jù)當(dāng)前可用內(nèi)存生成計劃,同時根據(jù)SQL引擎優(yōu)化器計劃生成過程中的cost估算值估算每個物化算子的內(nèi)存使用量,以及流水線場景下整個查詢使用的內(nèi)存總量估算值。如果該值大于當(dāng)前可用內(nèi)存,則嘗試將整個查詢的內(nèi)存使用量調(diào)到當(dāng)前可用內(nèi)存以下,此時會造成部分算子下盤。
(3)將語句及估算的語句內(nèi)存發(fā)送到CCN,如果當(dāng)前可用內(nèi)存小于語句估算內(nèi)存,則估算語句的內(nèi)存進(jìn)一步減少是否對查詢性能造成較大的影響,如果根據(jù)cost評估影響不大,則進(jìn)一步減少算子的內(nèi)存使用,使語句內(nèi)存使用滿足當(dāng)前可用內(nèi)存,將語句下發(fā)執(zhí)行,否則則進(jìn)入排隊(duì)狀態(tài)。
(4)由于每個算子的內(nèi)存使用量是基于cost評估獲得,可能存在一定的誤差。因此,在SQL語句執(zhí)行時,支持內(nèi)存的動態(tài)調(diào)整,包括:執(zhí)行算子內(nèi)存的自動擴(kuò)展和提前下盤。當(dāng)算子達(dá)到估算的內(nèi)存值上限,但系統(tǒng)還有寬裕的內(nèi)存時,會進(jìn)行算子內(nèi)存的擴(kuò)展,繼續(xù)保持不下盤的狀態(tài)。當(dāng)系統(tǒng)已用內(nèi)存達(dá)到80%或更高時,如果算子已有最小內(nèi)存保證,則會觸發(fā)提前下盤邏輯,保證不會由于內(nèi)存不足而報錯。
4.?????? GaussDB(DWS)內(nèi)存自適應(yīng)的使用和參數(shù)控制
通過開啟use_workload_manager和enable_dynamic_workload兩個參數(shù)開啟GaussDB(DWS)的內(nèi)存自適應(yīng)控制機(jī)制。
使用內(nèi)存自適應(yīng)機(jī)制時,打印SQL語句的explain performance執(zhí)行計劃運(yùn)行信息時,會包含以下額外的信息輔助定位問題:
(1)???????在最下方的Query Summary一欄中,會顯示出System available mem、Query max mem和Query estimated mem,分別表示:系統(tǒng)當(dāng)前可用內(nèi)存、語句可用最大內(nèi)存(系統(tǒng)可用最大內(nèi)存),語句估算內(nèi)存使用量,均為單DN的衡量值。下圖表示當(dāng)前語句的語句最大可用內(nèi)存和系統(tǒng)當(dāng)前可用內(nèi)存均為22G,語句估算內(nèi)存使用為1.6G。
(2)?????? 在Memory Information一欄,會顯示CN和每個DN的內(nèi)存使用峰值,如下圖所示,語句實(shí)際內(nèi)存使用,單DN使用16GB,CN使用76MB。
(3)?????? 在Memory Information一欄下方每個算子對應(yīng)的位置,會顯示每個算子單DN的內(nèi)存峰值,同時會顯示每個DN上內(nèi)存使用的自動擴(kuò)展和提前下盤情況,例如下圖,可以看出第15號HashJoin算子,每個SMP線程的內(nèi)存使用均為3.8GB,估算內(nèi)存是860MB,經(jīng)歷了五次內(nèi)存自動擴(kuò)展,在第五次擴(kuò)展后,系統(tǒng)內(nèi)存告急,算子未用到第五次擴(kuò)展后的峰值即提前下盤。
(4)?????? 在explain performance最頂層的表格中,匯總了每個算子的估算內(nèi)存和實(shí)際使用內(nèi)存的情況,見下圖的E-memory和Peak Memory兩列所示。與上面信息對應(yīng),第15號算子單SMP線程的peak memory,最大值為3766MB,最小值為3753MB,估算內(nèi)存值(單DN4個SMP線程)為860MB。
可以看出,上面例子由于cost估算不準(zhǔn)導(dǎo)致內(nèi)存估算值較小,實(shí)際場景也會出現(xiàn)內(nèi)存估算值較大的場景,會導(dǎo)致CCN預(yù)留內(nèi)存較多,阻塞其它作業(yè)的執(zhí)行。因此,可以使用參數(shù)query_mem來控制語句最大可用內(nèi)存上限(單DN),相當(dāng)于代替了Query max mem。此參數(shù)默認(rèn)為0,表示未開啟。當(dāng)此值大于32MB(最小語句內(nèi)存分配值)時,表示開啟,此時使用work_mem控制系統(tǒng)當(dāng)前可用內(nèi)存進(jìn)行估算,相當(dāng)于代替了System available mem進(jìn)行估算。此時,CCN會使用query_mem值進(jìn)行語句內(nèi)存估算值的預(yù)留和排隊(duì),提高并發(fā)場景下的內(nèi)存使用效率。
5.? ? ? ?總結(jié)
內(nèi)存自適應(yīng)控制技術(shù)是GaussDB(DWS)的資源管理結(jié)合SQL引擎所做的一次嘗試,當(dāng)然還存在一些不足,比如:cost估算對內(nèi)存的評估影響較大,部分場景存在失真需要進(jìn)行參數(shù)控制;系統(tǒng)中內(nèi)存使用情況比較復(fù)雜,還存在部分內(nèi)存不在管控范圍內(nèi)需要增強(qiáng)。歡迎各位在實(shí)用過程中,將遇到的各種問題及時反饋,也幫助我們更好的改進(jìn)!
SQL 數(shù)據(jù)庫 HUAWEI CONNECT
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。