摘要:本文用來總結一些GaussDB(DWS)在實際應用過程中,可能出現的各種作業排隊的情況,以及出現排隊時,我們應該怎麼去判斷是否正常,調整一些引數,讓資源分配與負載管理更符合當前的業務;或者在作業阻塞的時候,怎麼去處理這些情況,讓業務立刻恢復正常。
概述
資料庫系統的負載管理和資源管理,在整個系統中起著很重要的作用,比如很多使用者的業務壓力過大時,有時會導致連線數量被佔滿,有時會導致某種計算資源被佔滿,有時會導致儲存空間被佔滿,這些情況都會導致整個叢集進入異常甚至不可用的狀態:正在執行的作業互相爭搶CPU,會導致大家都不能好好執行;大量作業執行時,佔用大量記憶體,很容易觸發到記憶體瓶頸,造成作業記憶體不可用問題,導致業務報錯等等。在不進行併發控制的情況下,這些情況都很可能會出現,影響到正常業務。
本文用來總結一些GaussDB(DWS)在實際應用過程中,可能出現的各種作業排隊的情況,以及出現排隊時,我們應該怎麼去判斷是否正常,調整一些引數,讓資源分配與負載管理更符合當前的業務;或者在作業阻塞的時候,怎麼去處理這些情況,讓業務立刻恢復正常。
本文分為以下幾個小節去介紹並解答以上問題:
- 一、負載管理簡介:雙層排隊簡述
- 二、常用檢視以及使用方法介紹
- 三、負載管理常見問題以及處理
- 四、部分負載管理配置方案介紹
- 五、小結
一、負載管理簡介:雙層併發控制簡述
DWS的負載管理分為兩層,第一層為cn的全域性併發控制,第二層為資源池級別的併發控制。在通過第一層控制的時候,會繼續向前走到第二層資源池控制,根據資源池當前的負載資源情況決定作業繼續執行或者排隊。
DWS併發控制邏輯示意圖如下:
從本圖的邏輯我們可以看到,實際作業執行中,可能會在兩種佇列中排隊:
一種是全域性佇列(global queue),這種佇列不區分簡單和複雜作業,也不區分是DDL或者是普通語句,
一種是資源池佇列(resource pool queue),使用者下發的一般語句會根據資源消耗估算以及複雜程度在這裡進行判斷是否排隊。
在兩層佇列的過濾下,DWS會篩選出當前能執行的語句,使其正常執行,執行時也會受到其所屬資源池資源的限制(只能使用資源池配置的CPU、記憶體、IO配額)。
二、常用檢視以及使用方法介紹
這裡介紹幾個常用檢視以及SQL語句,可以迅速判斷目前的業務出現問題的原因,受限根據以下檢視可以看到目前的作業是不是在排隊,之後要迅速分析為什麼在排隊,是因為負載管理各個引數配置問題,還是因為正在執行的語句佔據了過多的資源導致的排隊。
- pgxc_stat_activity(pg_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,便於迅速進入下一步排查。
執行結果如下:
上圖解說:圖中為在沒有作業的情況下查詢,可以看到有幾個WLM的語句已經執行了很多,不過這兩個是內部常駐執行緒,所以不影響業務,也不會佔用連線數等資料庫資源。
其餘的行可以看到有bi使用者,在執行一些作業,時間大概1-6分鐘左右。
- pg_session_wlmstat (記錄每個cn的語句執行情況,資源使用情況,排隊情況等資訊)
常用語句:
#查詢每個使用者下的作業執行情況,排隊情況以及作業的複雜情況和記憶體消耗情況。 select usename,enqueue,datname,status,attribute,count(*),sum(statement_mem) from pg_session_wlmstat group by 3,1,2,4,5 order by 1,3,4,5 ;
執行結果如下:
上圖解說:
上圖中說明當前連線的cn中有使用者名稱為usr1的使用者執行作業,並且在資源池上排隊(第一節介紹的resourcepool佇列),資料庫為postgres,作業在排隊所以當前status為pending狀態。
其他欄位包括作業的屬性和當前使用記憶體資源的總和情況(根據該數值可以看出是否因為記憶體資源不足而進入排隊狀態)。作業在排隊時候不涉及到資源消耗,因此可能沒有具體數值。
- pgxc_thread_wait_status(檢視資料庫中各個執行緒的執行情況,當前在等待哪一步驟執行完成,在哪個例項等待,都可以通過該檢視查詢);
常用語句:
#在根據pgxc_stat_activity查詢到具體執行時間長的語句之後,可以根據本檢視查詢: select * from pgxc_thread_wait_status where query_id = xxxxxxxx;
該語句可以檢視執行慢語句的阻塞點,語句卡住時,可以通過該語句檢視語句卡在了哪個步驟。
舉例如下:
上圖可以看到作業在wait io,此時可以檢查下磁碟的讀寫速度是否,raid卡讀寫策略是否正常等等。
當出現排隊的時候,wait_status一般的狀態是waiting in ccn queue/waiting resourcepool queue等狀態。
- 查詢併發數量的方法,就是檢視pgxc_stat_activity中,狀態(state欄位)是active的語句,enqueue欄位為null的語句,除此之外,idle in transaction之類狀態的語句,也會佔用一個併發數量,因為一個事務塊的業務還未提交,自然也算是正在執行的業務,對於這種情況,後續可以考慮進行一個處於此類狀態的超時時間的控制。
三、負載管理常見問題
一般當業務上發現任務阻塞時,可以從後臺查詢部分語句排查目前的情況,此時要先看下現象上是什麼情況,一般併發控制有以下幾種現象,暫時先列出這麼多,後續繼續補充:
1. 業務反饋無法連線到資料庫,比如DS連線一直轉圈,過一段時間之後會超時報錯。
- 此時可以開始排查當前的配置情況,一般連線不到資料庫,其實是一種表象,主要是因為作業已經進入了排隊的狀態,語句本身就有很多在排隊,無法執行,此時用ds連線之後,ds本身就會下發語句查詢一些系統表之類的資訊,所以也會進入排隊的狀態,在客戶層面的影響就是ds一直處於一個連線中的狀態,實際上是ds的連線在資料庫中。
- 查詢引數配置,看當前每個cn可以接受的最大併發數量是多少:
- show max_active_statements;
如果當前cn的活躍作業超過這個引數,會出現語句排隊的情況,主要排隊情況為waiting in global queue,如下圖所示,下圖情況中,max_active_statements=6。
- 檢視業務連線是否主要只連線一個cn,可以同樣通過查詢pgxc_stat_activity判斷coorname,檢視是否大量的語句都是集中在某一個cn上執行,當語句下發集中在某個cn上時,這個cn的併發數量也只是max_active_statements設定的大小,舉個例子:如果業務連線3個cn,每個cn的max_active_statements設定都是10,那麼最大可以同時執行30個作業;如果這30個作業都下發到一個cn上,那麼就有20個排隊,同時只能執行10個,會大大影響執行效率,同時也會造成這種看似無法連線到該cn的現象。
- 解決方法:
- 業務負載不均:配置LVS,均衡業務,避免負載不均導致的排隊。
- max_active_statements引數設定過小:在資源負載允許的情況下繼續調大,如果資源達到瓶頸,繼續放大可能會導致資源爭搶,效果不大。
- 連線被空閒事務佔用:如上圖中情況,可以看到有很多連線的狀態都是idle in transaction,這種狀態處於事務塊完成但是沒有提交,也會佔用一個併發,導致其餘作業併發量減小,導致排隊。此類情況需要排查業務應用的執行情況,整改業務,避免處於這種空閒還佔用併發的情況。嚴重阻塞業務的情況,緊急處理可以將該類語句直接terminate掉。
- 確實已經有很多作業在執行,資源使用也已經達到一個瓶頸,這時候就需要去擴容或者降低業務量,來避免這種情況出現。
2. 作業長時間執行不出來,並且有很多作業在排隊,整體業務受到阻塞。
- 此類情況可以繼續使用上述查詢pgxc_stat_activity的語句,檢視語句的執行情況,結果可能如下:
如圖:圖中最下方的兩個作業,執行時間已經達到了11小時,但是仍然沒有執行完,後面還有未執行的語句在排隊。整體對於客戶來說就是資料庫已經hang死了,什麼作業都執行不出來。
排查步驟
- 排查當前叢集是否在使用動態自適應功能,檢視如下引數:
show enable_dynamic_workload; //檢視該引數目前是否為開啟狀態(2020年的版本都是預設開啟)
- 如果該引數開啟,那麼就要檢視這幾個正在執行作業目前的記憶體使用情況,大概率正是因為這幾個作業一直使用著記憶體不釋放,才會出現整體的排隊情況。
通過連線cn 5001查詢檢視:
select * from pg_session_wlmstat where threadid = $query_id;
該檢視可以看到某個語句使用了多大的記憶體,statement_mem欄位可以看到我這個語句是使用了1G,但是實際情況中,1個語句使用幾十個語句的情況很多。當一個或者幾個語句的記憶體達到整個叢集的總體可用記憶體上限,或者達到資源池上限的時候,就會使這些語句後面的語句開始排隊。資源不放,排隊不止。
- 處理方法
應急處理:及時將卡住的語句terminate,釋放其佔用的資源,後續進行優化之後再執行,優化手段很多,包括計劃調優,analyze等操作,都可以避免一個語句佔用太大記憶體,也可以提升執行效率。
3. 全域性資源可用記憶體很多,資源使用很小,併發數量也沒有達到上限,但是查詢顯示很多作業處於排隊狀態。
舉例:查詢pgxc_stat_activity時,語句的執行狀態如下:可以看到,所有語句都在waiting in ccn queue,只有兩個語句在執行。此時叢集設定的max_active_statements是40,但是該使用者實際活躍數量只有2個。
舉例:查詢pgxc_stat_activity時,語句的執行狀態如下:可以看到,所有語句都在waiting in ccn queue,只有兩個語句在執行。此時叢集設定的max_active_statements是40,但是該使用者實際活躍數量只有2個。
- 首先,出現出現waiting in ccn queue的情況,就一定是開啟了記憶體自適應功能(enable_dynamic_workload=on)。此時只有兩種情況,第一種是全域性資源不足,會排隊,第二種是使用者所屬的資源池記憶體不足,也會排隊。
- 根據圖中的現象,我們看到很多語句都在排隊,這時候要查詢一個排隊檢視:select * from pg_stat_get_workload_struct_info();
這個檢視可以清晰看到作業是在哪裡排隊:
首先,上圖中有totalsize,這個就是總體可用的最大記憶體,freesize_limit:執行語句可用的最大記憶體,freesize:當前可用的最大記憶體。
所以可以得出結論,此時不是在全域性排隊,全域性記憶體freesize足夠,那麼作業就是在資源池排隊,資源池排隊也會顯示waiting in ccn queue。
- 此時查詢pg_session_wlmstat檢視,可以看到正在執行的為dw使用者,正在執行的語句佔用的記憶體為兩條各1G。
可以通過介面或者後臺檢視一下,這個使用者對應的租戶上,是不是做了記憶體資源限制:
檢視租戶介面記憶體配置如下:
可以看到圖中給這個租戶或者說給這個使用者所在的資源池配置了10%的記憶體配額,一共是2277MB,即2G左右。
因此這種場景下,記憶體資源只能滿足當前兩個語句的執行,其餘屬於該使用者的語句都會進行排隊。
- 處理辦法:
- 方法一、此類問題,若在資源允許的情況下,可以適當調大租戶相關的記憶體資源,讓更多語句能夠同時執行。
- 方法二、優化相關語句的執行效率,縮短執行時間,讓語句可以高效執行,避免大量堆積。
4.執行的作業基本都是一個使用者的,其餘使用者的作業基本都在排隊中無法執行。
舉例:之前某局點出現高併發DDL的情況,該類高併發DDL在全域性併發計數中會受到統計,佔用max_active_statements中的併發數量。
比如:max_active_statements=20,其中某使用者一直持續不停地下發DDL,此時在DWS設計ap場景並未涉及此類場景。會出現該20個併發設定普遍被該使用者佔用的情況,其他使用者可能出現連不上的情況。
這種場景下,即時再調大併發,也會再被該使用者的DDL語句佔用。
處理方案:
針對高併發DDL的不合理業務場景進行優化,比如把連續建立刪除臨時表的動作,改為對永久表的操作,此時就可以極大幅度減少DDL併發數量,保證自身業務以及其餘使用者業務的正常連線和執行。
四、部分場景配置方案
1. 限制單使用者併發
使用者的作業分為以下幾種,DDL/DML/以及常規查詢,在DWS的視角中,常規查詢有分為簡單查詢和複雜查詢。
對於DWS的兩層管控,受到第一層管控的有DDL、start transaction、DML、常規查詢等,基本是所有語句,只有內部執行緒的語句(比如WLM執行緒)以及超級使用者許可權的使用者執行的作業。
受到第二層管控的語句,主要是可以從優化器獲取到具體執行計劃以及執行代價的語句。
除了DDL/start transaction 之類的語句,以及部分白名單語句,此類語句都是視為基本不消耗資源,並且不會造成阻塞的語句。
針對目前的管控機制來說,可以將單個使用者關聯到一個單獨的資源池,這個資源池不設定記憶體配置,只設定併發配置,達到進行作業管控的效果。
具體步驟及解釋如下:
1.在所有節點建立好控制組
gs_ssh -c "gs_cgroup -c -S class_a" gs_ssh -c "gs_cgroup -c -S class_a -G workload_a"
2.建立業務資源池(此時可以同步設定max_dop引數,active_statements會限制複雜作業的併發數量,max_dop引數會限制簡單作業的併發數量)
create resource pool p1 with (control_group="class_a:workload_a"); alter resource pool p1 with (active_statements=10,mem_percent=0,max_dop=1);
3.關聯使用者與該資源池
ALTER USER testuser RESOURCE POOL 'p1';
五、小結
併發管理的用處,主要是為了防止使用者跑太多的作業,導致叢集負載過大,資源發生爭搶,系統不能穩定執行,會因為資源出現各種問題,CPU/記憶體/IO,哪一個都是讓人頭疼的問題,併發控制也可以同步控制某一個使用者的併發作業,避免因為一個使用者的作業數量太大,導致其他使用者在使用資料庫的時候出現問題。
負載管理最主要的現象就是會出現作業排隊的情況,排隊是否合理,是否因為不合理的引數配置導致大量的業務阻塞;或者配置正常,但是沒有達到上限就出現排隊,本文的主要目的就是解決此類問題,或者及時定界到是什麼原因導致了阻塞。
實際使用過程中,許多時候出現排隊的情況,都可能和正在執行的語句有關,正在執行的語句佔用著這個位置,又執行不完,其餘語句自然會排隊。因此我們碰到語句大量阻塞的情況,要迅速定界到是因為什麼而造成阻塞,怎麼樣能恢復正常使用。找到問題的語句後,要及時去將它做一些適當的調優,避免再次執行到這個語句,還會出現一樣的情況。