詳解GaussDB(DWS)的CPU資源隔離管控能力
摘要:GaussDB使用cgroup實現了兩種cpu管控能力,基於cpu.shares的共享配額管控和基於cpuset的專屬限額管控。
本文分享自華為雲社群《GaussDB(DWS)的CPU資源隔離管控能力【這次高斯不是數學家】》,作者:門前一棵葡萄樹。
一、cgroup概述
cgroup全稱control group,是linux核心提供的用於對程序/執行緒使用的資源進行隔離、管控以及記錄的元件。
相關概念:
- 任務(task):對應系統中的一個程序/執行緒;
- 控制組(control group):進行資源限制隔離的基本單位,一個任務加入到控制組任務列表(tasks)後即受控制組資源控制,支援線上將一個任務從一個控制組遷移到另外一個控制組。
- 層級(hierarchy):控制組是一種樹形結構,一個父控制組可以有多個子控制組,一個子控制組只能屬於一個父控制組。同屬一個父控制組的子控制組間按照資源配置進行資源爭搶、隔離,子控制組繼承父控制組的資源配置。
- 子系統(subsytem):一個子系統對應一種資源的資源控制器,比如CPU子系統是控制CPU時間分配的控制器,CPUSET子系統是控制CPU核分配的控制器。
cgroup主要子系統介紹:
- cpu子系統:限制任務的cpu使用率;
- cpuacct 子系統:統計cgroup中所有任務使用cpu的累積資訊,單位ns;
- cpuset子系統:限制任務能夠使用的cpu核;
- memory子系統:限制任務的memory使用;
- blkio子系統:限制任務磁碟IO;
這裡我們著重介紹GaussDB應用到的cgroup子系統,涉及的子系統包括:cpu子系統、cpuacct 子系統以及cpuset子系統。
1.1 cgroup檔案系統
VFS (Virtual File System) 虛擬檔案系統是系統核心非常強大的一個功能,它把檔案系統的具體實現細節隱藏起來,給使用者態程序提供一個統一的檔案系統API介面。cgroup的介面操作也是基於VFS實現的,可以通過mount命令檢視cgroup掛載資訊,每個目錄對應cgroup的一個子系統。
1.1.1 cpu & cpuacct 子系統
cpu子系統和cpuacct子系統相輔相成,cpu子系統限制cgroup使用的cpu時間,cpuacct統計cgroup使用的cpu時間,同時cpu子系統與cpuacct子系統掛載路徑一致,因此可以把這兩個子系統放在一起討論:
tasks
tasks記錄著關聯到該cgroup的任務(程序/執行緒)pid,只有加入tasks的任務才受cgroup控制。
cpu子系統用於控制cgroup中所有任務可以使用的cpu時間,主要包含以下幾個介面:
cpu.cfs_period_us & cpu.cfs_quota_us
cfs_period_us 與cfs_quota_us 需要組合使用,cfs_period_us用來配置進行cpu限制的單位時間週期,cfs_quota_us用來配置在設定的時間週期內所能使用的CPU時間。二者單位都是微秒(us),cfs_period_us的取值範圍為1毫秒(ms)到1秒(s),cfs_quota_us的取值大於1ms即可,如果cfs_quota_us的值為-1(預設值),表示不受cpu週期的限制。舉例說明:
1. 限制只能使用1個CPU(每100ms能使用100ms的CPU時間,即可以使用一個cpu) # echo 100000 > cpu.cfs_quota_us /* quota = 100ms */ # echo 100000 > cpu.cfs_period_us /* period = 100ms */ 2. 限制使用3個CPU(核心)(每100ms能使用300ms的CPU時間,即可以使用兩個cpu) # echo 300000 > cpu.cfs_quota_us /* quota = 300ms */ # echo 100000 > cpu.cfs_period_us /* period = 100ms */ 3. 限制使用1個CPU的30%(每100ms能使用30ms的CPU時間,即可以使用30%的cpu) # echo 30000 > cpu.cfs_quota_us /* quota = 30ms */ # echo 100000 > cpu.cfs_period_us /* period = 100ms */
cpu.shares
shares用來設定cgroup中任務CPU可用時間的相對比例,針對所有可用cpu,預設值是1024,在cpu出現滿負載爭搶時,各控制組按照shares設定的相對比例爭搶cpu。假如系統中有兩個cgroup,分別是A和B,A的shares值是10000,B的shares值是20000,那麼A和B出現cpu爭搶時A將獲得10000/(10000+20000)=33.3%的CPU資源,而B將獲得66.7%的CPU資源。shares作為一種共享配額的cpu管控方式,具備以下幾個特點:
- cpu空閒情況下,shares不起作用,只有在cpu出現滿負載爭搶時,各控制組任務才按照shares配置比例爭搶cpu
- 空閒cpu其他控制組可以使用,如果A沒有使用到33.3%的cpu時間,那麼剩餘的cpu時間將會被分配給B,即B的CPU使用率可以超過66.7%
- 如果添加了一個新的控制組C,且它的shares值是10000,那麼A的配額比例變成10000/(10000+20000+10000)=25%,B的cpu配額比例變成50%
- 由於shares是一個權重值,需要和其它控制組的權重值進行對比才能得到自己的配額比例,在一個負載多變的環境上,cgroup數量和權重可能是多變的,這樣給cgroup配置的配額比例,可能因為增加cgroup或修改其他cgroup權重導致該cgroup配額比例發生變化,無法精確控制cpu使用率。
cpu.stat
stat包含以下三項統計結果
- nr_periods: 表示經歷了多少個cpu.cfs_period_us裡面配置的時間週期
- nr_throttled: 在上面的這些週期中,有多少次cpu受到了限制(即cgroup中的程序在指定的時間週期中用光了它的配額)
- throttled_time: cgroup中的程序被限制使用CPU持續了多長時間(納秒)
cpu.rt_runtime_us & cpu.rt_period_us
rt_runtime_us與rt_period_us組合使用可以對cgroup中的實時排程任務進行cpu時間限制,只可用於實時排程任務。rt_period_us用來配置進行cpu限制的單位時間週期,設定每隔多久cgroup對cpu資源的存取進行重新分配,rt_runtime_us用來配置在設定的時間週期內任務對cpu資源的最長連續訪問時間,二者單位都是微秒(us)。
cpuacct(cpu accounting)子系統用於統計cgroup中任務所使用的cpu時間,主要包含以下介面:
cpuacct.usage
統計cgroup中所有任務使用的cpu時間,單位ns,可以通過寫入0值重置統計資訊
cpuacct.usage_percpu
統計cgroup中所有任務在每個cpu核上使用的cpu時間,單位ns
cpuacct.stat
統計cgroup中所有任務使用的使用者態和系統態cpu時間,單位USER_HZ,格式如下:
- user:cgroup中所有任務使用的使用者態cpu時間
- system:cgroup中所有任務使用的核心態cpu時間
1.1.2 cpuset 子系統
cpuset子系統可以為cgroup分配專屬的cpu核和記憶體節點,cgroup中所有任務只能執行在分配的cpu和記憶體節點上。GaussDB僅應用了cpuset子系統的cpu控制能力,因此我們這裡僅介紹cpu相關控制介面。
cpuset.cpus
設定cgroup中任務可以使用的cpu,使用小橫線‘-’設定連續cpu,不連續cpu之間使用逗號‘,’分隔。例如:0-1,11-12,17 表示cgroup可以使用cpu 0、1、11、12、17。
cpuset.cpu_exclusive
包含標籤0和1,可以控制其他cpuset及其父子cpuset是否可以共享該cpuset的cpu,預設值為0,cpu不會專門分配給某個cpuset。
cpuset.sched_load_balance
包含標籤0和1,設定核心是否可以在該cpuset的cpu上進行負載均衡,預設值1,表示核心可以將超載cpu上的任務移動至低負載cpu上以平衡負載。
注意:如果父cgroup啟用了負載均衡,則其所有子cgroup預設開啟負載均衡,因此如果要禁用cgroup的負載均衡,則其所有上層cgroup都需要關閉負載均衡,同時需要考慮其他同層cgroup是否能夠關閉負載均衡。
cpuset.sched_relax_domain_level
表示核心進行負載均衡的策略,如果禁用負載均衡則該值無意義。不同系統框架下,該值意義可能不同,以下為常用值的含義:
二、GaussDB的cpu管控
GaussDB支援兩種cpu管控能力,基於cpu.shares的共享配額管控和基於cpuset的專屬限額管控。在介紹GaussDB的cpu管控之前,我們首先介紹GaussDB中的cgroup結構。
2.1 GaussDB的cgroup層級模型
GaussDB的cpu管控需要考慮其他程序與GaussDB之間的cpu管控,GaussDB核心後臺執行緒與使用者執行緒之間的cpu管控,使用者之間的cpu管控。為了應對不同層級的cpu隔離管控需求,GaussDB設計了基於cgroup層級特點的cpu分層隔離管控,GaussDB的cgroup層級模型如下圖所示:
以上cgroup均適配了cpu子系統和cpuset子系統,每一個cgroup都包含兩個值:cpu.shares和cpuset.cpus。GaussDB藉助cgroup提供了三個維度的cpu隔離管控能力:
- GaussDB與其他程序之間的隔離管控
- 資料庫常駐後臺執行緒與作業執行緒的隔離管控
- 資料庫使用者之間的隔離管控
GaussDB與其他程序之間的隔離管控
資料庫叢集的每個節點在cgroup的cpu子系統和cpuset子系統內均包含一個專屬目錄:“GaussDB:gaussdba”,作為GaussDB的主cgroup節點(目錄),用於限制和記錄GaussDB內所有執行緒使用的cpu,GaussDB程序內所有執行緒均直接或間接的受到該cgroup的限制。通過限制GaussDB核心使用的cpu,可防止資料庫系統對其他應用程式造成影響。
資料庫常駐後臺執行緒與作業執行緒的隔離管控
GaussDB主cgroup節點之下包含兩個cgroup:Backend控制組和Class控制組。Backend控制組用於GaussDB常駐後臺執行緒的cpu隔離管控,Class控制組用於作業執行緒的cpu隔離管控。大部分後臺常駐執行緒cpu資源佔用較少,但AutoVacuum執行緒cpu資源佔用可能較多,因此在Backend控制組單獨提供Vacuum控制組用於AutoVaccum執行緒的cpu資源限制,而除AutoVacuum之外的其他後臺常駐執行緒均受到DefaultBackend控制組的cpu資源限制。
資料庫使用者之間的隔離管控
通常情況下資料庫系統會同時執行多種型別的作業,不同型別作業之間可能出現cpu資源爭搶。GaussDB資源管理為使用者提供了雙層的cgroup層級結構用於cpu隔離管控,使用者可按需建立和修改cgroup配置實現不同型別作業之間的cpu資源限制。使用者雙層cgroup包含以下控制組:
- UserClass控制組:使用者建立的父控制組,主要實現cpu資源的初步劃分,如:不同的父控制組可以屬於不同的部門/分公司
- RemainWD控制組:包含UserClass控制組建立UserWD控制組後剩餘的cpu資源
- Timeshare控制組:建立UserClass控制組時預設建立的優先順序控制組,包含四個優先順序的控制組:Rush:High:Medium:Low,資源配比為:8:4:2:1
- UserWD控制組:使用者建立的子控制組,在父控制組基礎上實現cpu資源的細粒度劃分,如:不同的子控制組可以屬於同一部門下的不同型別作業
2.2 GaussDB作業的cpu管控
資料庫系統中執行多種型別作業出現cpu爭取時,不同使用者可能有不同的訴求,主要訴求如下:
- 實現cpu資源的充分利用,不在意單一型別作業的效能,主要關注cpu整體吞吐量
- 允許一定程度的cpu資源爭搶和效能損耗,在cpu空閒情況下實現cpu資源充分利用,在cpu滿負載情況下希望各型別按比例使用cpu
- 部分作業對效能敏感,不在意cpu資源的浪費
對於第一種訴求,不建議進行使用者之間的cpu隔離管控,不管控哪一種的cpu管控都或多或少的對cpu整體使用率產生影響;對於第二種訴求可以採用基於cpu.shares的共享配額管控方式,實現滿負載cpu隔離管控前提下儘量提高cpu整體使用率;對於第三種訴求可以採用基於cpuset.cpus的專屬限額管控方式,實現不同型別作業之間的cpu絕對隔離。下面對這兩種作業cpu管控方式進行簡要介紹,具體細節和使用中的疑問,我們後面文章再詳細介紹,用興趣的朋友也可以自己動手實驗。
2.2.1 CPU的共享配額管控
共享配額有兩層含義:
共享:cpu是所有控制組共享的,空閒的cpu資源其他控制組能夠使用
配額:業務繁忙cpu滿負載情況下,控制組之間按照配額比例進行cpu搶佔
共享配額基於cpu.shares實現,通過上面cgroup的介紹我們可知這種管控方式只有在cpu滿負載情況下生效,因此在cpu空閒情況下並不能保證控制組能夠搶佔到配額比例的cpu資源。cpu空閒是不是可以理解為沒有cpu資源爭搶,控制組內任務可以任意使用cpu,因此不會有效能影響呢?答案是錯誤的,雖然cpu平均使用率可能不高,但是某個特定時刻還是可能存在cpu資源爭搶的。示例:
10個cpu上執行10個作業,每個cpu上執行一個作業,這種情況下各作業在任意時刻請求cpu都可以瞬間得到響應,作業之間沒有任何cpu資源的爭搶;但是假如10個cpu上執行20個作業,因為作業不會一直佔用cpu,在某些時間可能等待IO、網路等,因此cpu使用率可能並不高,此時cpu資源看似空閒,但是在某個時刻可能出現2~N作業同時請求一個cpu的情況出現,此時即會導致cpu資源爭搶,影響作業效能。
通過測試驗證,在cpu滿負載情況下,控制組之間基本可以按照配額比例佔用cpu,實現cpu資源的配額管控。
2.2.2 CPU的專屬限額管控
專屬限額有兩層含義:
專屬:cpu是某個控制組專屬的,空閒的cpu資源其他控制組不能使用
限額:只能使用限額配置的cpu資源,其他控制組空閒的cpu資源,也不能搶佔
專屬限額基於cpuset.cpus實現,通過合理的限額設定可以實現控制組之間cpu資源的絕對隔離,各控制組間任務互不影響。但是因為cpu的絕對隔離,因此在控制組空閒時就會導致cpu資源的極大浪費,因此限額設定不能太大。那從作業效能來看是不是限額越大越好呢?答案是不完全正確,示例:
假設10個作業執行在10個cpu上,cpu平均使用率5%左右;10個作業執行在5個cpu上,cpu平均使用率10%左右。通過上面共享配額的效能分析我們可知:雖然10個作業執行在5個cpu上cpu使用率很低,看似空閒,但是相對10個作業執行在10個cpu上還是存在某種程度的cpu資源爭搶的,因此10個作業執行在10個cpu上效能要好於執行在5個cpu上。那是不是越多越好呢?10個作業執行在20個cpu上,在任意一個時刻,總會至少10個cpu是空閒的,因此理論上10個作業執行在20個cpu上並不會比執行在10個cpu上效能更好。
因此我們可以得知,對於併發為N的控制組,分配cpus小於N的情況下,cpu越多作業效能越好;但是當分配cpus大於N的情況下,效能就不會有任何提升了。
2.2.3 共享配額與專屬限額對比
cpu共享配額和專屬限額的管控方式各有優劣,共享配額能夠實現CPU資源的充分利用,但是各控制組之間資源隔離不徹底,可能影響查詢效能;專屬限額的管控方式可以實現cpu資源的絕對隔離,但是在cpu資源空閒時會造成cpu資源的浪費。相對專屬限額來說,共享配額擁有更高的cpu使用率和更高的整體作業吞吐量;相對共享配額來說,專屬限額cpu隔離徹底,更滿足效能敏感使用者的使用訴求。
從上面GaussDB的cgroup層級結構我們得知,使用者cgroup是包含父子兩層控制的,那可不可以父控制組一層使用專屬限額,而子控制組一層使用共享配額呢?答案是肯定的,另外同一層控制組也可以同時有使用專屬限額和共享配額的控制存在。具體使用本文不做介紹,有興趣的可以自己試驗。
【這次高斯不是數學家】有獎徵文火熱進行中:https://bbs.huaweicloud.com/blogs/345260
華為夥伴暨開發者大會2022火熱來襲,重磅內容不容錯過!
【精彩活動】
勇往直前·做全能開發者→12場技術直播前瞻,8大技術寶典高能輸出,還有程式碼密室、知識競賽等多輪神祕任務等你來挑戰。即刻闖關,開啟終極大獎!點選踏上全能開發者晉級之路吧!
【技術專題】
未來已來,2022技術探祕→華為各領域的前沿技術、重磅開源專案、創新的應用實踐,站在智慧世界的入口,探索未來如何照進現實,乾貨滿滿點選瞭解