一文詳解GaussDB(DWS) 的並行管控和記憶體管控

2022-11-24 15:00:29
摘要:DWS的負載管理分為兩層,第一層為cn的全域性並行控制,第二層為資源池級別的並行控制。

本文分享自華為雲社群《GaussDB(DWS) 並行管控&記憶體管控》,作者: fighttingman。

1背景

這裡將並行管控和記憶體管控寫在一起,是因為記憶體管控實際是通過限制語句的並行達到記憶體管控的目的的。記憶體管控是基於語句的估算記憶體的前提下進行管控的,通俗的說就是語句有個估算記憶體,當資源池的剩餘記憶體小於語句的估算記憶體時,這個語句就會排隊等待,等資源池內的語句執行完,資源池有足夠的剩餘記憶體的時候,才會讓這個語句執行。所以記憶體管控的實際效果和語句的估算記憶體有很大關係,估算的大了就會造成大量語句排隊,實際沒有使用那麼多記憶體,造成記憶體資源浪費,相反估算的小了,就會有很多語句下發,實際記憶體使用就會變多,就有語句報記憶體不足的錯誤風險。

資料庫系統的並行控制,在整個系統中起著很重要的作用,比如很多使用者的業務壓力過大時,有時會導致連線數量被佔滿,有時會導致某種計算資源被佔滿,有時會導致儲存空間被佔滿,這些情況都會導致整個叢集進入異常甚至不可用的狀態:正在執行的作業互相爭搶CPU,會導致大家都不能好好執行;大量作業執行時,佔用大量記憶體,很容易觸發到記憶體瓶頸,造成作業記憶體不可用問題,導致業務報錯等等。在不進行並行控制的情況下,這些情況都很可能會出現,影響到正常業務。

2 總體介紹

DWS的負載管理分為兩層,第一層為cn的全域性並行控制,第二層為資源池級別的並行控制。在通過第一層控制的時候,會繼續向前走到第二層資源池控制,根據資源池當前的負載資源情況決定作業繼續執行或者排隊。

基於DWS並行控制邏輯看出,實際作業執行中,可能會在兩種佇列中排隊:

一種是全域性佇列(global queue)這種佇列不區分簡單和複雜作業,也不區分是DDL或者是普通語句,這種是每個cn生效。

一種是資源池佇列(resource pool queue),使用者下發的一般語句會根據資源消耗估算以及複雜程度在這裡進行判斷是否排隊。

在兩層佇列的過濾下,DWS會篩選出當前能執行的語句,使其正常執行,執行時也會受到其所屬資源池資源的限制(只能使用資源池設定的CPU、記憶體、IO配額)。

3 全域性排隊

這裡介紹幾個常用檢視以及SQL語句,可以迅速判斷目前的業務出現問題的原因,受限根據以下檢視可以看到目前的作業是不是在排隊,之後要迅速分析為什麼在排隊,是因為負載管理各個引數設定問題,還是因為正在執行的語句佔據了過多的資源導致的排隊。

pgxc_stat_activity (活躍檢視)

查詢當前執行時間最長的語句的排隊狀態,query_id(資料庫中作業的唯一標識),以及詳細的語句資訊。

select coorname,usename, current_timestamp-query_start as duration, enqueue,query_id,query from pgxc_stat_activity where state='active' and usename <> 'Ruby' order by duration desc;

根據該語句可以迅速判斷出哪些語句執行時間很長,是什麼樣的語句執行很慢以及該語句的query_id,便於迅速進入下一步排查。

該檢視中enqueue欄位中如果顯示waiting in global queue就代表在全域性排隊。全域性排隊是受GUC引數max_active_statements引數控制的,是單cn生效的,也就是每個cn都可以支援這麼大的並行量。比如叢集中有3個cn範例,GUC引數max_active_statements引數設定為60,也就是說每個cn都支援60個語句並行執行,叢集全域性支援3 * 60 = 180並行執行作業。當下發作業大於這個cn設定的max_active_statements的時候就會進行全域性排隊,在pgxc_stat_activity檢視中enqueue欄位就會顯示waiting in global queue。

4 資源池排隊

4.1 靜態負載管理

當GUC引數enable_dynamic_workload設定為off的時候就代表是靜態負載管理模式。靜態負載管理的情況下,pgxc_stat_activity檢視中enqueue欄位只會有waiting in respool queue。並行控制引數為資源池的max_dop(簡單作業)和active_statements(複雜作業)。

1)簡單作業和複雜作業的定義

在靜態負載管理中,簡單作業是估算代價cost值小於GUC引數parctl_min_cost值的作業。反之則判定為複雜作業。該GUC引數預設為10W,

當parctl_min_cost為-1時,或者作業估算代價小於10時,作業都判定為簡單作業。

2)簡單作業並行限制

ALTER RESOURCE POOL resource_pool_a1 WITH (max_dop=10);

通過設定資源池的max_dop引數設定簡單作業並行,關聯資源池resource_pool_a1的使用者都受到這個引數的控制。當所有關聯這個資源池的使用者的所有作業數量之和大於這個引數的時候,就會進行資源池排隊,活躍檢視enqueue欄位就會顯示waiting in respool queue。

3)複雜作業並行限制

ALTER RESOURCE POOL resource_pool_a2 WITH (active_statements=10);

通過設定資源池的active_statements引數控制複雜作業的並行數,關聯資源池resource_pool_a2的使用者都受到這個引數的控制。

  • 當MEM_PERCENT引數數值為0時,ACTIVE_STATEMENTS為x(1~INT_MAX),該資源池上的作業並行數不大於x。
  • 當ACTIVE_STATEMENTS引數數值為-1且MEM_PERCENT為正值時,並行由執行作業的記憶體估值和MEM_PERCENT的取值決定。
  • 當MEM_PERCENT引數數值為正值且ACTIVE_STATEMENTS為x(1~INT_MAX)時,並行由執行作業的記憶體估值和MEM_PERCENT的取值決定,且並行不能大於x。
  • 當MEM_PERCENT引數數值為0且ACTIVE_STATEMENTS為-1時,資源池並行不受限。

資源池使用並行點數的計數方式來計算可執行的複雜作業並行數量,並行點數計算公式為

作業使用記憶體點數:active_points = (query_mem/respool_mem) * active_statements * 100

作業使用並行點數:active_points = 100

資源池總點數:total_points = active_statements * 100

單位點數: 100

4)相關說明

  • 資源池分快慢車道,快車道管控簡單作業,慢車道管控複雜作業
  • MAX_DOP對快車道並行進行限制,取值範圍為-1 ~ INT_MAX,預設為-1,表示不管控。
  • ACTIVE_STATEMENTS取值範圍為 -1 ~ INT_MAX, 預設值為10,建議使用該預設值。當取值設定為0或者-1時,慢車道並行不受ACTIVE_STATEMENTS限制。
  • MEM_PERCENT取值範圍為0~100,當取值設定為0時,慢車道並行不受MEM_PERCENT限制。
  • 慢車道並行受ACTIVE_STATEMENTS和MEM_PERCENT限制,同時點數由ACTIVE_STATEMENTS決定。當ACTIVE_STATEMENTS=-1或0時,total_points=90。total_points點數耗盡後,慢車道查詢會觸發排隊操作,佇列滿足先進先出。
  • query_mem為優化器估算的作業記憶體大小,即PG_SESSION_WLMSTAT檢視中的statement_mem;作業無估算記憶體數值時,不進行並行控制。
  • respool_mem為資源池的實際記憶體。

4.2 動態負載管理

當GUC引數enable_dynamic_workload設定為on的時候就代表是動態負載管理模式。動態負載管理的情況下,pgxc_stat_activity檢視中enqueue欄位會有waiting in respool queue和waiting in global queue。

1)簡單作業和複雜作業的定義

動態負載管理下優化器估算記憶體大於32M認為是複雜作業,反之認為是簡單作業。

執行中的作業複雜簡單情況可以通過PG_SESSION_WLMSTAT中的attribute欄位檢視。

2)動態負載管理相關說明

  • 叢集有一個CN會作為中心協調節點(CCN),用於收集和排程作業執行,該節點可以通過cm_ctl query -Cv查詢到,Central Coordinator State會顯示其狀態。當CCN不存在時,作業不再受動態負載管理控制。
  • CCN上包含全域性記憶體管控佇列和資源池佇列,目前暫不支援跨佇列優先順序,在以下場景下優先順序低的作業可能優先下發:如果優先順序高的作業在全域性記憶體管控佇列排隊,優先順序低的作業在資源池佇列排隊,則優先順序低的作業會優先下發。
  • 單CN上依然受到max_active_statements引數限制,但不是強制限制,實際執行的作業可能稍微大於該數值。
  • 簡單查詢作業(估算值<32MB)、非DML(即非INSERT、UPDATE、DELETE和SELECT)語句,不走自適應負載,需要通過max_active_statements來進行單CN的上限控制。
  • 預設work_mem數值為512MB,在自適應負載特性下,該數值不能變大,否則會引起記憶體不受控(例如未做Analyze的語句)。
  • 作業估算記憶體小於等於0時,如果強制將作業指定為慢車道管控,作業不會發往CCN管控將直接執行。
  • 以下場景或語句由於記憶體使用特殊性和不確定性,可能導致大並行場景記憶體不受控,如果遇到需要降低並行數。
  • 單條元組佔用記憶體過大的場景,例如,基表包含超過MB級別的寬列。
  • 完全下推語句的查詢。
  • 需要在CN上耗費大量記憶體的語句,例如,不能下推的語句,withhold cursor場景。
  • 由於計劃生成不當導致hashjoin運算元建立的hash表重複值過多,佔用大量記憶體。
  • 包含UDF的場景,且UDF中使用大量記憶體的場景。

3)短查詢加速(預設開啟,建議開啟)

混合負載場景下,複雜查詢可能會長時間佔用大量資源,雖然簡單查詢執行時間短、消耗資源少,但是因為資源耗盡,簡單查詢不得不在佇列中等待複雜查詢執行完成。為提升執行效率、提高系統吞吐量,GaussDB(DWS)的「短查詢加速」功能,實現對簡單查詢的單獨管理。

  • 開啟短查詢加速後,簡單查詢與複雜查詢分開管理。
  • 關閉短查詢加速後,簡單查詢與複雜查詢執行相同的工作負載管理。

雖然單個簡單作業資源消耗少,但是大量簡單作業並行執行還是會佔用大量資源,因此短查詢加速開啟情況下,需要對簡單查詢進行並行管理;資源管理可能會影響查詢效能,影響系統吞吐量,因此簡單查詢不進行資源管理,異常規則也不生效。

設定方法:

  • 通過GUC引數wlm_query_accelerate設定
  • 通過資源池alter resource pool query_pool with(short_acc='f');

4.3 資源池記憶體管理

資源池的記憶體管理是基於語句的估算記憶體進行管理的。

1)資源池可用記憶體設定方法

ALTER RESOURCE POOL resource_pool_a1 WITH (MEM_PERCENT=20);
  • 當MEM_PERCENT引數取值設定為0時,表示查詢作業的記憶體不受限。
  • 當MEM_PERCENT引數取值設定為"x"(1<=x<=100)時,表示設定資源池使用的記憶體大小為可用記憶體大小的"x%",查詢作業將使用給定的記憶體來執行。

2)資源池作業估算記憶體限制設定方法

ALTER RESOURCE POOL resource_pool_a1 WITH (MEMORY_LIMIT="300MB");
  • 當MEMORY_LIMIT引數取值設定為unlimited時,表示作業記憶體受資料庫記憶體限制。
  • 當MEMORY_LIMIT引數取值設定為default時,表示作業記憶體限制為資源池記憶體的1/2。
  • 當MEMORY_LIMIT引數取值設定為x kB/MB/GB時,表示作業記憶體限制為xkB/MB/GB。
  • 當memory_limit設定小於256M時,為防止估算記憶體過小導致問題,作業估算記憶體上限為256MB。

5 資源管理相關檢視

GaussDB(DWS)對外提供諸多系統檢視,可以用來輔助資源管理及資源使用相關問題的分析定位,常用檢視及用法說明如下表所示。(☆代表常用程度)

除過上述常用檢視,資源管理問題定位過程需要根據實際場景,結合範例紀錄檔、叢集狀態等共同分析定位。

6 推薦設定

因為並行的設定和業務的複雜程度和叢集的規格設定有很大的關係,本推薦僅做參考。推薦基於3CN 12DN,每個dn範例最大可使用64G記憶體情況下推薦的

在813核心版本及以上版本推薦設定如下。

GUC引數:

  • max_active_statements 60 (每個cn的最大並行數,控制全域性佇列排隊)
  • enable_dynamic_workload on (開啟動態負載)
  • wlm_query_accelerate -1 (開啟短查詢加速)

資源池引數:

  • ALTER RESOURCE POOL resource_pool_a1 WITH (MAX_DOP=50) (簡單作業數50並行)
  • ALTER RESOURCE POOL resource_pool_a1 WITH (active_statements=10) (複雜作業10並行)

7 並行控制常用定位方法及解決措施

7.1 排隊問題

出現業務阻塞、效能下降、查詢無響應等類似現網問題時,通過以下方法可以排查是否排隊問題並定位排隊原因,同時根據排隊原因給出相應規避措施。

7.1.1 確認是否排隊

首先確認是否排隊問題,其次排查排隊原因,確認是否屬於正常排隊:

  • 813及以上版本查詢資源池監控檢視
select rpname,slow_run,slow_wait,slow_limit,used_cpu,cpu_limit,used_mem,estimate_mem from gs_respool_resource_info;
  • 老版本查詢作業負載檢視
select resource_pool,attribute,lane,status,enqueue,sum(statement_mem) as stmt_mem,count(1) from pgxc_session_wlmstat where status!='finished' and attribute!='Internal' and usename!='Ruby' group by 1,2,3,4,5;

通過檢視可以獲取到各資源池快慢車道作業執行資訊,據此可以判斷是否排隊問題:

如果有作業處於排隊狀態,則可能是排隊導致的問題,否則排除排隊問題;可能的排隊原因包括:

  • 單CN全域性並行排隊;
  • 快車道並行排隊;
  • 靜態慢車道並行排隊;
  • 靜態慢車道記憶體排隊;
  • 動態CCN全域性記憶體排隊;
  • 動態CCN慢車道並行排隊;
  • 動態CCN慢車道記憶體排隊。

排查排隊原因

常見排隊原因及解決措施

1)全域性並行排隊

單CN實際執行作業數≥全域性並行上限,則全域性並行排隊正常;

單CN實際執行作業數長時間小於全域性並行上限,則可能存在計數洩露。

2)快車道排隊

快車道實際執行作業數≥快車道並行上限,則快車道並行排隊正常;

快車道實際執行作業數長時間小於快車道並行上限,則可能存在計數洩露。

3)靜態慢車道排隊

慢車道實際執行作業數≥慢車道並行上限,則慢車道並行排隊正常;

慢車道實際執行作業累計估算記憶體≥慢車道記憶體上限,則慢車道記憶體佔用達到上限導致排隊,關注是否有查詢估算記憶體過大;

如果慢車道並行和記憶體佔用長時間達不到上限,則可能存在計數洩露。

4)動態CCN排隊

如果查詢在CCN排隊,則需要查詢CCN開發者檢視確認排隊原因:

select * from pg_stat_get_workload_struct_info();

CCN上可能的排隊原因:

  • CCN全域性可用記憶體不足導致排隊,此時需特別關注是否有查詢估算記憶體過大;
  • 資源池實際執行作業數≥慢車道車道並行上限,資源池並行上限,正常排隊;
  • 資源池實際執行作業累計估算記憶體≥慢車道記憶體上限,則慢車道記憶體佔用達到上限導致排隊,此時需特別關注是否有查詢估算記憶體過大;
  • 資源池實際執行作業數或佔用記憶體與記賬值不符,則可能存在計數洩露BUG;
  • 隊首作業在CCN雜湊中不存在,說明隊首作業殘留導致查詢不能正常下發;
  • CN/CCN處於recover狀態或收集DN記憶體資訊失敗(多CCN)導致所有查詢等待5s下發,現象為所有查詢排隊時間均為5~6s。

8 常見案例

8.1 CCN排隊

1)查詢資源池監控檢視,確認是否正常排隊(813及以上版本)

下面以單CN下發作業為例,多CN下發作業需查詢pgxc_respool_resource_info檢視。

select rpname,slow_run,slow_wait,slow_limit,used_cpu,cpu_limit,used_mem,estimate_mem from gs_respool_resource_info;
  • 如果slow_wait不等於0,說明有查詢在CCN排隊,否則無查詢排隊;
  • 如果slow_run大於等於slow_limit,說明達到慢車道並行上限導致排隊,否則說明不是並行過大導致排隊;
  • 如果estimate_mem大於資源池記憶體上限,說明記憶體不足導致排隊,否則說明不是記憶體不足導致排隊;
  • 如果used_mem長時間遠小於estimate_mem,說明該資源池上執行作業估算記憶體過大,可以嘗試analyze;
  • 如果used_mem大於estimate_mem,則查詢可能觸發記憶體二次擴充套件(預設資源池)或查詢記憶體不可控;
  • 如果used_cpu長時間接近甚至大於等於cpu_limit,說明資源池分配CPU過少,可能導致作業大量堆積;

通過該查詢可以直觀的觀察各資源池作業負載資訊,如果資源池running作業並行、記憶體長時間無法達到資源池上限,則考慮是否存在排隊異常。

2)查詢作業負載檢視(813以下版本)

813及以上版本建議使用上邊方法確認是否有排隊異常,當然也可以使用以下方法確認存在排隊異常,排除特性BUG影響。

813以下版本僅有pg_session_wlmstat檢視,沒有pgxc檢視,可通過以下語句建立臨時pgxc檢視:

CREATE OR REPLACE VIEW pgxc_session_wlmstat_tp AS
SELECT * FROM pg_catalog.pgxc_parallel_query('cn', 'SELECT pg_catalog.pgxc_node_str(), * FROM pg_catalog.pg_session_wlmstat') AS (
 nodename           name,
 datid oid,
 datname            name,
 threadid bigint,
 processid          integer,
 usesysid oid,
 appname            text,
 usename            name,
        priority           bigint,
        attribute          text,
 block_time bigint,
 elapsed_time bigint,
 total_cpu_time bigint,
 cpu_skew_percent   integer,
 statement_mem      integer,
 active_points      integer,
 dop_value          integer,
 control_group      text,
        status             text,
        enqueue            text,
 resource_pool      name,
        query              text,
 is_plana boolean,
 node_group         text,
        lane               text
);

查詢叢集內各資源池在所有CN上的作業執行、排隊統計資訊:

select resource_pool,attribute,lane,status,enqueue,sum(statement_mem) as stmt_mem,count(1) from pgxc_session_wlmstat where status!='finished' and attribute!='Internal' and usename!='Ruby' group by 1,2,3,4,5;

通過該查詢可以直觀的觀察各資源池作業負載資訊,如果資源池running作業並行、記憶體長時間無法達到資源池上限,則考慮是否存在排隊異常。

確認是否存在排隊異常

如果經過前兩個步驟分析,懷疑可能存在排隊異常,可能的原因有以下幾種:

  • 大批作業一開始執行就報錯退出,依靠CCN週期任務完成作業同步和喚醒;
  • CCN全域性記憶體排隊導致資源池並行、記憶體長時間無法達到資源池上限。

 

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