GlusterFS性能優化-讓目錄飛

根據IDC預測2020年全球數據量將達到44ZB,其中80%來自於非結構化數據的貢獻。隨着雲計算、大數據、物聯網、AI、5G等技術的發展應用,可快速擴展的基礎架構成爲必需,這些需求推動了軟件定義存儲(SDS)的增長。 2018年中國軟件定義存儲市場需求場景中,文件存儲仍然是主力,佔比高達62.3%。

在諸多分佈式文件系統中,GlusterFS以其簡約的架構設計,完善的協議支持,無中心節點、全局統一命名空間、高可用、高性能、橫向擴展等特點,擁有着旺盛的生命力,在工業界受到極大的歡迎和使用。然而因其無中心的架構設計,導致其目錄ls性能一直被用戶所詬病。

基於這樣的背景,本人及所在TaoCloud團隊針對GlusterFS目錄ls性能低的問題進行分析研究,提出了幾種優化方案。根據方案的相關性及複雜度制定出了我們的研發計劃,並已完成了階段性目標,極大的提升了GlusterFS目錄ls的性能。

問題分析

與中心控制節點架構的分佈式文件系統不同,GlusterFS的元數據是分散存儲於每一個brick上面的,所以其目錄ls操作無法直接從元數據節點獲取所需信息,而是需要分別向所有brick下發請求以獲得元數據信息。

 對GlusterFS目錄ls流程分析,其存在以下影響時延的因素:

1. 無元數據中心,導致每次ls要分別向所有brick發起readdir請求。

2. Ls操作經過vfs解析變成size爲4k的多次連續readdir請求。

3. 訪問路徑冗長,要經過多次內核態與用戶態的交互及網絡通信。

4. GlusterFS內部鎖機制(如EC卷的eager-lock)。

GlusterFS社區對目錄ls操作已經進行了一些優化,例如增加了readdir-ahead和parallel-readdir功能,可將每次readdir請求size設置爲128k,有效的減少了client與server之間的網絡通信次數,並且預讀下一個readdir(next_off, size),當fuse下發下一個readdir時,可從readdir-ahead層的緩存中直接返回。針對EC的eager-lock,增加了other-eager-lock參數,用於分離讀寫與ls之間的鎖等。

 然而,即使這樣,GlusterFS的目錄ls操作時延,在許多情況下依然超過用戶的容忍極限。

在此基礎上,我們討論出以下幾種優化方案:

1. 在GlusterFS之上增加一個通用型的元數據中心,作爲GlusterFS之上的插件使用。該方案不依賴與GlusterFS,作爲一個獨立的系統架構。可供其他需要元數據管理的系統使用。該方案原型設計複雜度較高,工作量較大,項目週期較長。但效果顯著,能解決包括ls在內的大多數元數據操作性能問題,且可作爲插件供其他系統使用,通用性較高。

2. 在GlusterFS的Client端增加元數據中心,用於保存GlusterFS的元數據,當請求下發到Client時,直接從元數據中心獲取所需信息。該方案基於GlusterFS系統本身實現,原型設計複雜度沒有方案1那麼高,但多Client一致性問題處理起來較爲複雜,且可能對其他操作造成一定的性能影響,項目週期也較長,但ls性能優化效果明顯,可解決包括ls在內的大多數元數據操作性能問題。

3. 在GlusterFS的Server端增加元數據緩存,各Server端進程只緩存該brick的目錄項及元數據,不用考慮多Client一致性問題。該方案基於GlusterFS系統本身實現,在Server端增加一個translator用以緩存目錄項及元數據。用戶readdir(p)請求下發到此translator時,直接根據緩存信息處理請求並返回,而無需繼續下發到本地文件系統,這樣以訪問高速緩存替代低速磁盤,並減少一次用戶態與內核態交互的方式,來減少ls的時延。該方案優化效果雖不如前兩種方案明顯,但亦有極大的提升,且項目週期較短,適合作爲突破口。

 經過實驗及深入分析,發現在readdir過程中,GlusterFS在Server端posix-translator向本地文件系統發起readdir請求耗時較多,約佔整個訪問流程時延的2/3,且在磁盤讀寫壓力大的情況下,該操作時延增加明顯。綜合用戶需求及項目週期等因素,儘量做到短期內對ls性能有較大的提升效果,最終選擇方案3來實現GlusterFS目錄ls性能優化,同時繼續進行對方案1,2的調研工作,並作爲下一步工作重點及優化方向。

  

·方案設計

軟件架構:

相關模塊功能說明如下:

· GlusterFS ClientGlusterFS客戶端進程,負責處理/dev/fuse操作請求(如readdir操作),並與GlusterFS Server進程交互,完成對應用請求的處理。其中,已有的readdir-ahead功能位於客戶端進程中,用於預讀目錄項及其元數據信息,並可設置rda-request-size爲128k,減少請求次數。而parallel-readdir功能包含在readdir-ahead功能,可以通過參數設置啓用或禁用。

·  GlusterFS Server:GlusterFS服務端進程,負責接收並處理GlusterFS客戶端進程的請求,對外提供基於POSIX協議的存儲服務。其中,如圖2所示,在已有posix層之上,我們引入一個dcache層(即dcache translator),用於緩存和處理brick端的目錄結構和文件屬性和擴展屬性元數據信息。

 

Dcache設計

Dcache設計目的是爲了解決用戶目錄ls時延高的問題,所以該方案中Dcache緩存了目錄項及元數據信息。用於處理並返回readdir(p)的請求,而無需繼續下發的posix訪問磁盤。對於其他獲取元數據的請求(如lookup),並不在dcache中處理。

考慮到服務器內存限制,Dcache方案並未將目錄項及元數據信息全部保存於內存當中。而是僅將目錄結構信息保存於內存當中,而將元數據保存到嵌入式KV數據庫levelDB當中。

如圖3,GlusterFS在Server端處理readdir(p)請求時,Dcache在步驟2取代了虛線部分posix層訪問本地文件系統的操作,以高速緩存代替低速磁盤訪問,並減少一次用戶態與內核態交互的方式,減少readdir(p)時延。

 內存目錄結構

如圖4,爲目錄在Dcache內存中的組織結構。

GlusterFS中每個目錄項對應一個dentry。dentry的data信息中保存了該dentry的gfid, name, pargfid, d_type等信息。

 通過目錄項的gfid,採用hash算法,定位到dentry_table->gfid_hash[hv]。並通過gfid,name, pargfid信息確定該目錄項對應的dentry。若該dentry爲目錄,則其child_list鏈表下鏈接的爲該目錄下所有目錄項。該內存結構即可滿足readdir請求所需的信息。            

DB元數據信息

DB以每個dentry的gfid爲key, 屬性及擴展屬性爲value的方式保存了所有目錄項的元數據信息。

通過Dcache的內存結構,可獲取其目錄結構,即可返回readdir請求。而若是readdirp請求,則需要該目錄下每個目錄項的元數據信息(屬性及擴展屬性信息)。此時需要以目錄項的gfid爲key訪問KV數據庫levelDB獲取元數據信息。

 

·方案實現

Dcache緩存目錄項及元數據的設計採用了非持久化的設計方案,每次進程啓動都需要重新加載目錄項及元數據到緩存,而不依賴於GlusterFS Server進程上次運行緩存的結果,從而很大程度的減少了程序設計與實現的難度與複雜度。

Dcache實現主要分爲三部分:預加載模塊,任務處理模塊,readdir(p)相關模塊。

1. 預加載模塊在存儲節點啓動時,加載brick上已有的目錄項及其元數據信息到內存目錄結構及DB中。

2. 任務處理模塊爲處理與元數據相關的ops在dcache中的邏輯。

3. Readdir(p)相關模塊處理了opendir, readdir(p), releasedir的邏輯。

 

預加載模塊

該模塊從根目錄開始,採用深度優先算法遍歷所有目錄項。如圖5所示,爲獲取其中一個目錄項的處理步驟:

1. 從磁盤獲取目錄項及屬性和擴展屬性信息。

2. 爲該目錄項創建相應的內存結構

3. 以該目錄項的gfid爲key, 屬性和擴展屬性信息爲value存入數據庫當中。

重複以上步驟,直到遍歷結束。根據測試,加載過程耗時不多(每個brick上500萬目錄項2min左右),且加載階段不影響系統對外提供服務,只是該階段內dcache未生效,不對外提供ls加速效果。

 

任務處理模塊

分析了GlusterFS所有ops,從而篩選出與元數據處理相關的ops在Dcache中進行處理。爲儘量減少增加dcache後對正常讀寫等邏輯的性能影響,可將ops分爲兩類:

1. 目錄項增刪相關的ops: create, mkdir, mknod, link, rmdir, unlink, symlink, rename。

2. 目錄項元數增刪改相關ops: open(O_TRUNC), writev, (f)truncate, (f)setattr, (f)setxattr, (f)removexattr, zerofill, discard, (f)xattrop, fallocate。

      其中目錄項增刪相關的ops操作採用同步處理機制,以便目錄ls時,能夠獲取到最新的結果。目錄項元數據增刪改相關的ops操作採用異步聚合處理機制,以最大限度的減少此類操作對正常邏輯的性能影響(如小粒度的追加寫操作會頻繁的更新元數據)。經測試,增加dcache後,對系統正常讀寫邏輯幾乎沒有影響。

 

·目錄項增刪處理

爲保證目錄ls時能夠及時獲取到最新的結果,以避免出現剛創建的目錄項查看不到,或者查看到已刪除的目錄項的情況,故目錄項增刪相關的ops採用同步處理機制。

如圖6所示,在一個ops流程中,當posix在本地文件系統執行完成,向上回調時,dcahe在回調函數中,根據posix的處理結果,執行相應操作,以保證dcache中目錄項及元數據與本地文件系統一致。具體ops的處理細節略有不同,此處不進行代碼實現細節的贅述。

·目錄項元數據增刪改處理

爲避免dcache操作內存及數據庫影響系統對外提供服務的性能,將及時性要求不是特別高的目錄項元數據增刪改操作採用異步聚合處理機制。

該模塊中需要在初始化時創建一個後臺線程worker thread,用於異步處理ops操作生成的任務。

如圖7所示,對於目錄項元數據的增刪改對應的ops,在dcache層回調時,根據不同的ops生成相應的任務(task), 並將該任務的必要信息保存到task中,然後加入隊列(queue)中等待後臺線程(worker thread)處理。

Worker thread線程,會順序的從queue中取task,並根據task中的信息,進行相應的邏輯處理,具體ops對應的處理邏輯略有不同,此處不進行代碼實現細節的贅述。

此處有兩點需要特別注意:

異步處理邏輯的目的就是爲了儘量減少元數據更新操作對系統性能的影響,所以,在生成task並加入queue中時,此處做了查重處理,對於同一個目錄項的同類型任務進行聚合,最後只進行一次處理,大幅度減少了後臺線程元數據操作的次數,減輕系統負載。如:同一文件的大量連續的小粒度追加寫操作,可聚合爲一次元數據更新。

因異步處理元數據信息,導致,若該task未處理,而有readdirp請求需要獲取該目錄項的元數據信息,此時,從DB中獲取到的元數據信息可能不是最新的。若用戶的後續操作依賴於該readdirp獲取到的元數據信息,則會出錯。所以,針對這種情況,在readdir(p)模塊進行了特殊處理,以避免這種問題的出現。

 

Readdir(p)模塊

如問題分析中所述,一個ls請求會被分爲許多次連續的readdir(off, size)請求。其中off記錄了上次請求的位置,size爲本次請求的大小。

如圖4:內存目錄結構所示,該目錄結構設計中並無off相關的設計,所以無法記錄上次readdir的位置,且若同一個目錄被多用戶ls時,每個用戶都會持有自己的off,且在readdir過程中有目錄項的增刪,都會對off造成影響。

針對這種情況,設計了遊標結構dfd。用於標記readdir的位置,其在opendir時,創建該遊標結構dfd,並將該遊標保存於opendir的fd結構當中。遊標dfd隨着readdir移動。當遊標dfd移動到尾部時,readdir返回結束標誌。收到releasedir時,釋放該遊標。

 

·Readdir(p)邏輯

如圖8所示,當opendir時dfd指向其要打開目錄的dentry。

Readdir(p)邏輯:

當off爲0時,以遊標dfd指向的dentry->child_list爲head,向後遍歷該鏈表,以size計算該次遍歷結束的標誌。此時,dfd也隨着dentry的遍歷而移動,off也隨之進行加操作。

當off不爲0時,以遊標dfd指向的dentry->brother爲head,向後遍歷該鏈表,以size爲該次遍歷結束的標誌。此時,dfd也隨着dentry的遍歷而移動,off也隨之進行加操作。

若遇到dentry->brother的下一個爲打開目錄的dentry,則此次遍歷結束,並且返回readdir結束的標誌。

每遍歷一個dentry,則可從其data信息中獲取到readdir所需的所有信息,若爲readdirp請求,則以data中的gfid爲key,訪問DB,獲取其value並填充屬性就擴展屬性信息。然後返回readdirp請求。

注:若該目錄下有新增dentry的操作,則新增dentry加入隊列尾部,不影響後續的readdir。若該目錄下有刪除dentry的操作,則將指向被刪除的dentry的dfd向前移動一位,指向上一個dentry,亦不影響後續的readdir操作。

 

·特殊處理

在4.2.2中已經提到過,因目錄項元數據更新採用異步處理機制,所以其readdirp獲取到的目錄項的元數據可能不是最新的。針對這種情況,在readdir(p)模塊進行了特殊處理。

在dcache設計時,因考慮到性能的問題,對於目錄項元數據相關ops並未採用同步處理機制。但也不能完全不考慮一致性的問題。

所以,在這裏採用了一種相對摺中的方式,當opendir時,下發一個強制執行queue中屬於該目錄下目錄項的任務。由一個後臺處理線程process thread去執行該任務。

當收到readdir(p)請求時,判斷強制執行任務是否執行完成,若完成,則可進行readdir(p)操作,未完成則等待。因queue中任務進行了聚合操作,所以一般情況下,任務都是很少的,基本不會出現等待時間過長的情況。

 

 ·測試環境與方法

測試環境

硬件環境:

軟件環境:

OS: CentOS Linux release 7.6.1810 (Core)

Gluster: glusterfs-3.12.15-1.el7.x86_64

工具:vdbench

 

集羣環境:

EC:3 * (2+1)

雙副本: 3 * 2

 

測試方法

測試均在開啓readdir-ahead, parallel-readdir, readdir-optimize, nl-cache的情況下進行。

靜態無讀寫情況測試:

1. 原生系統,目錄下不同數量級的文件個數ls耗時。

2. Dcache方案,目錄下不同數量級的文件個數ls耗時。

 

動態有讀寫(1個壓力客戶端進行vdbench 64線程隨機讀寫(6:4),一個測試客戶端進行ls測試):

1. 原生系統,目錄下不同數量級的文件個數ls耗時。

2.Dcache方案,目錄下不同數量級的文件個數ls耗時。

 

·結果與結論

測試結果

1 EC卷測試結果

 

2 副本卷測試結果

 

·結果與結論

根據測試結果,可得出如下結論:

EC模式

1. 在靜態無讀寫情況下,增加dcache方案,ls性能約提升1-2倍

2. 在動態有讀寫情況下,原生系統ls性能表現出不穩定的特點,受系統壓力及磁盤狀態影響嚴重。

3. 在動態有讀寫情況下,dcache方案表現穩定,基本不受讀寫壓力的影響。

4. 在動態有讀寫情況下,增加dcache方案,ls性能約提升7-57倍,且隨着讀寫壓力增大,提升愈加明顯。

 

副本模式

1. 在靜態無讀寫情況下,增加dcache方案,ls性能約提升2-5倍

2. 在動態有讀寫情況下,原生系統ls性能表現出不穩定的特點,受系統壓力及磁盤狀態影響嚴重。

3. 在動態有讀寫情況下,dcache方案表現穩定,基本不受讀寫壓力的影響。

在動態有讀寫情況下,增加dcache方案,ls性能約提升15-82倍,且隨着讀寫壓力增大,提升愈加明顯。

 

總結與展望

綜合以上測試結果及結論,可以看出dcache方案對ls提升效果明顯,且表現穩定,不隨系統壓力或磁盤狀態而出現嚴重影響ls時延的現象。

在實際應用場景中,這種ls不受系統壓力影響,且時延隨文件個數近似線性增長的現象,能夠給用戶以合理的時間預期,而不會出現ls時延超出用戶容忍極限或者死等待的情況,極大的提升了用戶體驗。

該方案雖然對ls性能有很大提升,但其還存在許多不足之處:

1. 訪問路徑冗長、交互次數過多、需要訪問所有brick的問題依然存在。

2. 無讀寫狀態下,ls性能優化效果有限。

3. 僅解決了目錄ls的性能問題,並不能解決其他元數據操作的性能問題(如:lookup, fstat等)。

由此可見,GlusterFS目錄ls性能還存在很大的優化空間,且GlusterFS元數據性能問題也有待解決,所以,我們以此爲突破口,計劃下一步做位於GlusterFS之上的通用型元數據中心,從根本上解決GlusterFS元數據性能問題,並希望可以將其應用到其他需要元數據管理的系統當中。

 

參考文獻

1. https://mp.weixin.qq.com/s/MtXTIPDwBZ1ZfUExK0mdnA《GlusterFs元數據機制分析》

2. https://blog.csdn.net/liuhong1123/article/details/8126451《Glusterfs之小文件優化 》 

3. https://blog.csdn.net/inevity/article/details/24873165《Glusterfs目錄ls性能優化方案分析》

(TaoCloud團隊原創)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章