OOM(Out of Memory)描述的是Query的記憶體消耗超出了系統當前的供給,系統做出的一種異常提示。本文將為您介紹如何在Hologres中查看記憶體消耗、分析記憶體水位高情況,識別OOM(記憶體溢出)現象及其產生原因,並提供相應的處理方法。
記憶體消耗分析
查看記憶體消耗
管理主控台提供整個執行個體的記憶體消耗情況,即多個節點的記憶體匯總值,詳情請參見Hologres管控台的監控指標。
慢Query日誌中memory_bytes欄位提供單個Query的記憶體消耗情況,是非精確的值,存在一定的誤差,詳情請參見慢Query日誌查看與分析。
記憶體水位高
可以通過Hologres管控台的
記憶體使用量率
和執行個體記憶體分布使用率
指標瞭解執行個體的記憶體綜合使用率,詳情請參見Hologres管控台的監控指標。當記憶體水位長期超過80%,可以認為屬於記憶體水位高的情況。Hologres的記憶體資源採用預留模式,即使沒有執行查詢操作,也會有部分Meta、Index中繼資料和Cache載入到記憶體中,該類別中繼資料用於提升計算速度,無任務運行時記憶體使用量率可能會到達30%~50%左右,屬於正常現象。記憶體使用量率持續升高,甚至接近100%,通常會影響系統的運行,影響執行個體的穩定性和效能。關於該問題產生的原因、主要影響和解決方案具體如下:產生原因
中繼資料佔用記憶體多
表現為Meta記憶體使用量率高:表資料量增加,資料總量也隨之增加,中繼資料佔用記憶體多,當沒有任務運行時,記憶體水位也會高,通常建議一個Table Group下不要超過10000張表(包括分區子表,不包含外部表格),Table Group的Shard數高,也會造成更多的檔案片段和積累更多的中繼資料,佔用中繼資料記憶體。
計算記憶體高
表現為Query記憶體使用量率高:運行任務時掃描巨量資料量或者計算邏輯非常複雜,例如有非常多的Count Distinct函數、複雜的Join操作、多欄位Group By、視窗操作等。
主要影響
影響穩定性
當存在中繼資料過大等問題時,會超額佔據正常Query可用的記憶體空間,導致在查詢過程中,可能會偶發
SERVER_INTERNAL_ERROR、ERPC_ERROR_CONNECTION_CLOSED、Total memory used by all existing queries exceeded memory limitation
等報錯。影響效能
當存在中繼資料過大等問題時,會超額佔據正常Query可用的緩衝空間,從而導致快取命中減少,Query延遲增加。
解決方案
中繼資料過多導致記憶體較高時,建議通過
hg_table_info
表對資料表進行治理,詳情請參見表統計資訊查看與分析。建議刪除不再查詢的資料或者表和減少不必要的分區表設計,以釋放佔用的記憶體。計算導致記憶體水位較高時,建議區分寫入和查詢情境,進行SQL最佳化,詳情請參見如何解決查詢時OOM和如何解決匯入匯出時OOM。
擴容,對執行個體的計算和儲存資源進行升配,詳情請參見執行個體列表。
識別OOM報錯
當計算記憶體超出上限時(大於等於20 GB),就會出現OOM的情況。常見的報錯如下。
Total memory used by all existing queries exceeded memory limitation.
memory usage for existing queries=(2031xxxx,184yy)(2021yyyy,85yy)(1021121xxxx,6yy)(2021xxx,18yy)(202xxxx,14yy); Used/Limit: xy1/xy2 quota/sum_quota: zz/100
報錯解讀如下。
queries=(2031xxxx,184yy)
指
queries=(query_id,query使用的記憶體)
,例如queries=(2031xxxx,18441803528)
,代表query_id=2031xxxx
的Query,在運行時單個節點使用了18 GB的記憶體。異常資訊裡面會列出消耗記憶體的Top 5的Query,可以通過報錯找到記憶體消耗最大的Query,並在慢Query日誌查看與分析中查看詳細的Query資訊。Used/Limit: xy1/xy2
指
單個節點使用的計算記憶體/單個節點的計算記憶體上限
,單位為Byte。單個節點使用的計算記憶體是指當前時刻所有Query運行時在該節點使用的計算記憶體總和。例如Used/Limit: 33288093696/33114697728
,代表所有Query在該節點運行時的記憶體使用量了33.2 GB,但是單個計算節點的彈性記憶體只能配33.1 GB,因此出現OOM。quota/sum_quota: zz/100
quota
代表資源群組,其中zz
對應資源群組分配的資源。例如quota/sum_quota: 50/100
代表設定了資源群組,其分配的資源是執行個體總資源的50%。
產生OOM的基本原因
有的系統在記憶體資源不足時會採用磁碟緩衝的方式進行運算元降級(Spill to Disk),Hologres為了保障查詢的效率,預設所有運算元都採用記憶體資源進行計算,因此會存在記憶體不足導致OOM的問題。
記憶體的分配和上限
一個Hologres執行個體是由多個節點群組成的分布式系統,不同的執行個體規格對應不同的節點數,詳情請參見執行個體規格概述。
在Hologres中一個節點的規格是16 Core 64 GB,即記憶體上限是64 GB,一個Query運行時涉及到的任意節點的記憶體不足,都會產生OOM異常。記憶體會分幾個部分,包括Query計算、後端進程、緩衝、Meta等部分。在早期版本中,計算節點(Worker Node)的記憶體上限是20 GB,但Hologres從V1.1.24版本開始,計算節點運行時記憶體取消單節點20 GB的限制,採用動態調整節點記憶體,定期檢查記憶體水位,如果中繼資料較少時,會盡量將剩餘可用記憶體都分配給查詢運行時使用,盡量保證運行時記憶體最大化分配,保障Query獲得足夠記憶體配置。
如何解決查詢時OOM
當出現查詢OOM時,其原因通常有如下幾類。
執行計畫錯誤:統計資訊不正確、Join Order不正確等。
Query並發度高,且每個Query消耗的記憶體很大。
Query本身複雜或者掃描的資料量很大。
Query中包含
union all
,增加執行器的並行度。設定了資源群組,但是給資源群組分配的資源較少。
資料扭曲或者Shard Pruning導致負載不均衡,個別節點的記憶體壓力較大。
以上原因的具體分析以及相應的解決方案如下。
資源群組分配的資源較少
使用Serverless Computing功能執行查詢。Serverless Computing功能可以實現在執行個體獨享資源之外,使用Serverless資源執行相關查詢。相比執行個體獨享資源,Serverless Computing提供更豐富的計算資源,並且查詢之間不會相互爭搶資源,因此非常適合解決記憶體溢出(OOM)問題。Serverless Computing介紹,詳情請參見Serverless Computing概述,其使用方式詳情,請參見Serverless Computing使用指南。
檢查執行計畫是否合理
類型1:統計資訊不準確
通過執行
explain <SQL>
可以查詢執行計畫,如下圖所示rows=1000
表示缺少統計資訊或者統計資訊不準確,會導致產生不準確的執行計畫,從而使用更多的資源進行計算造成OOM。解決方案如下。
執行
analyze <tablename>
命令,更新表的統計資訊。設定AUTO ANALYZE自動更新統計資訊,詳情請參見ANALYZE和AUTO ANALYZE。
類型2:Join Order不正確
當兩個表通過Hash運算元執行串連時,合理的串連方式是資料量較小的表構建Hash表。通過執行
explain <SQL>
命令查看執行計畫,如果資料量更大的表在下方,小表在上方時,表示使用更大的表構建Hash表,這種Join Order容易導致OOM。Join Order不正確的原因通常如下。表未及時更新統計資料,例如下圖中上面的表沒有更新統計資料,導致
rows=1000
。最佳化器未能產生更好的執行計畫。
解決方案如下。
對參與Join的表都執行
analyze <tablename>
命令,及時更新統計資料,使其產生正確的Join Order。執行
analyze tablename
命令之後,Join Order還是不正確,可以通過修改GUC參數進行幹預。如下所示設定optimizer_join_order=query
,使最佳化器按照SQL的書寫順序確定Join Order,適用於複雜Query。SET optimizer_join_order = query; SELECT * FROM a JOIN b ON a.id = b.id; -- 會將b作為HashTable的build side
同時也可以根據業務情況,調整Join Order策略。
參數
說明
set optimizer_join_order = <value>
最佳化器Join Order演算法,values有如下三種。
query:不進行Join Order轉換,按照SQL書寫的串連順序執行,最佳化器開銷最低。
greedy:通過貪心演算法進行Join Order的探索,最佳化器開銷適中。
exhaustive(預設):通過動態規划算法進行Join Order轉換,會產生最優的執行計畫,但最佳化器開銷最高。
類型3:Hash表預估錯誤
當有Join操作時,通常是會把小表或者資料量小的子查詢作為Build Side構建Hash表,這樣既能最佳化效能,又能節省記憶體。但是有時候因為查詢過於複雜,或者統計資訊的問題,資料量會估錯,就導致把資料量大的表或者子查詢做了Build Side,這樣一來,構建Hash表會消耗大量的記憶體,導致OOM。
如下圖所示,執行計畫中
Hash (cost=727353.45..627353.35 , rows=970902134 width=94)
即為Build Side,rows=970902134
就是構建Hash表的資料量,若是實際表資料量比這個少,說明Hash表預估不準確。解決方案如下。
查看子查詢的表是否更新統計資料或者統計資訊是否準確,若是不準確,請執行
analyze <tablename>
命令。通過以下參數關閉執行引擎對Hash表的預估。
說明該參數預設關閉,但是可能在某些調優情境被開啟過,若是查看時開啟的,可以進行關閉。
SET hg_experimental_enable_estimate_hash_table_size =off;
類型4:大表被Broadcast
Broadcast是指將資料複製至所有Shard。僅在Shard數量與廣播表的數量均較少時,Broadcast Motion的優勢較大。在Join情境中,執行計畫先進行Broadcast,即將Build Side的資料廣播完再構建Hash表,這就意味著每個Shard內構建Hash表的資料都是Build Side的全量資料,若是Shard多或者資料量較大,則會消耗很多記憶體,造成OOM。
假如表資料量是8000萬行,如下圖執行計畫所示,預估表只有1行,參與Broadcast只有80行,與真實情況不符合,真實執行時需要8000萬行資料參與Broadcast,導致消耗過多記憶體從而出現OOM。
解決方案如下。
檢查執行計畫中預估行數是否正確,不正確的話,請執行
analyze tablename
命令更新統計資料。通過以下GUC關閉Broadcast,直接改寫為redistribution運算元。
SET optimizer_enable_motion_broadcast = off;
Query並發大
監控指標上QPS增加明顯,或者OOM中報錯:
HGERR_detl memory usage for existing queries=(2031xxxx,184yy)(2021yyyy,85yy)(1021121xxxx,6yy)(2021xxx,18yy)(202xxxx,14yy);
且每個Query使用的記憶體較少,說明當前Query並發較大,可以通過以下方式解決。若是有寫入,可以降低寫入並發,詳情請參見如何解決匯入匯出時OOM。
擴容執行個體計算規格。
複雜Query
若是Query本身比較複雜或者掃描資料量較多,一個Query就出現OOM,可以通過以下方法解決。
計算前置,將清洗好的資料寫入Hologres,避免在Hologres進行大型ETL操作。
增加過濾條件。
SQL最佳化:例如使用Fixed Plan、Count Distinct最佳化等,詳情請參見最佳化查詢效能。
UNION ALL
如下所示,當SQL中含有大量
UNION ALL subquery
時,執行器會平行處理每個subquery
,導致記憶體壓力變大,從而出現OOM。subquery1 UNION ALL subquery2 UNION ALL subquery3 ...
可以通過如下參數強制執行器串列,減少OOM情況,但查詢速度會變慢。
SET hg_experimental_hqe_union_all_type=1; SET hg_experimental_enable_fragment_instance_delay_open=on;
資源群組配置不合理
OOM時出現報錯:
memory usage for existing queries=(3019xxx,37yy)(3022xxx,37yy)(3023xxx,35yy)(4015xxx,30yy)(2004xxx,2yy); Used/Limit: xy1/xy2 quota/sum_quota: zz/100
。其中zz的取值較小,如下圖所示為10,說明資源群組只擁有執行個體10%的資源。解決方案:重新設定資源群組配額,每個資源群組都不應該小於
30%
。資料扭曲或Shard Pruning
當執行個體整體記憶體水位不高,但仍然出現OOM的情況,一般原因為資料扭曲或者Shard Pruning導致某個或者某幾個節點的記憶體水位較高,從而出現OOM。
說明Shard Pruning是指通過查詢剪枝技術,只掃描部分Shard。
通過以下SQL排查資料扭曲,
hg_shard_id
是每個表的內建隱藏欄位,表示資料所在的Shard。SELECT hg_shard_id, count(1) FROM t1 GROUP BY hg_shard_id;
從執行計畫查看Shard Pruning,例如執行計畫中Shard Selector為
l0[1]
,說明只選中了一個Shard資料進行查詢。-- 這裡distribution_key為x, 基於x=1過濾條件,可以快速定位所在Shard SELECT count(1) FROM bbb WHERE x=1 GROUP BY y;
解決方案如下。
設計合理的Distribution Key避免資料扭曲。
若是業務有資料扭曲,需要對業務進行改造。
大基數多階段GROUP BY
從Hologres V3.0版本開始,對於大基數的多階段Agg,當GROUP BY的列與資料分布不匹配(Distribution Key不是GROUP BY Key的子集)時,低階段Agg的每個並發執行個體都會維護一張很大的Hash Table做GROUP BY Key彙總,導致記憶體壓力很大,容易OOM。可以通過設定如下參數分階段去進行Agg操作。
-- 通過guc設定Agg HashTable行數的上限,如下SQL表示partial_agg_hash_table最多時8192行。預設值為0,表示不限制。 SET hg_experimental_partial_agg_hash_table_size = 8192;
如何解決匯入匯出時OOM
匯入匯出OOM是指資料在Hologres表之間匯入匯出,也包括內部表和外部表格之間匯入匯出,常見於MaxCompute匯入到Hologres時出現OOM。
使用Serverless Computing功能執行匯入匯出
Serverless Computing功能可以實現在執行個體獨享資源之外,使用Serverless資源執行相關匯入匯出任務。相比執行個體獨享資源,Serverless Computing提供更多的計算資源,並避免了任務之間的資源爭搶,非常適合解決記憶體溢出(OOM)問題。Serverless Computing概述請參見Serverless Computing概述,其詳細使用方法請參見Serverless Computing使用指南。
大寬表或者寬列且有高Scan並行度
通常在MaxCompute匯入情境會出現大寬表或者比較寬的列有比較大的Scan並行度,導致寫入出現OOM。可以通過以下參數控制匯入並行度減少OOM。
大寬表匯入(常用情境)
說明以下參數與SQL一起執行(優先選擇前兩個參數,若是仍然出現OOM,可以適當調低參數取值)。
-- 設定訪問外部表格時的最大並發度,預設為執行個體的Core數,最大為128,不建議設定大,避免外部表格Query(特別是資料匯入情境)影響其它Query,導致系統繁忙導致報錯。該參數在Hologres V1.1及以上版本中生效。 SET hg_foreign_table_executor_max_dop = 32; -- 調整每次讀取MaxCompute表batch的大小,預設為8192。 SET hg_experimental_query_batch_size = 4096; -- 設定訪問外部表格時執行DML語句的最大並發度,預設值為32,針對資料匯入匯出情境專門最佳化的參數,避免匯入操作佔用過多系統資源,該參數在Hologres V1.1及以上版本中生效。 SET hg_foreign_table_executor_dml_max_dop = 16; -- 設定MaxCompute表訪問切分split的數目,可以調節並發數目,預設64MB,當表很大時需要調大,避免過多的split影響效能。該參數在Hologres V1.1及以上版本中生效。 SET hg_foreign_table_split_size = 128;
比較寬的列有比較大的Scan並行度
若是已經調整過大寬表的匯入參數,但是仍然出現OOM,可以排查業務是否有比較寬的列,若有可以通過調整以下參數解決。
-- 調整寬列的shuffle並行度,減少寬列資料量的堆積 SET hg_experimental_max_num_record_batches_in_buffer = 32; -- 調整每次讀取MaxCompute表batch的大小,預設8192。 SET hg_experimental_query_batch_size=128;
外部表格資料重複
若是外部表格包含大量重複資料,會導致匯入速度變慢或出現OOM。重複資料是相對而言,並沒有統一標準,例如有1億行資料,有8000萬行資料都是重複的,則認為重複資料較多,請根據實際業務情況進行判斷。
解決方案:匯入之前先對資料進行去重再進行匯入或者分批次匯入,避免一次性匯入大量重複資料。