HDFS 細粒度鎖優化,FusionInsight MRS有妙招
摘要:華為雲FusionInsight MRS通過FGL對HDFS NameNode鎖機制進行優化,有效提升了NameNode的讀寫吞吐量,從而能夠支援更多資料,更多業務請求訪問,從而更好的支撐政企客戶高效用數,業務洞見更準,價值兌現更快。
本文分享自華為雲社群《FusionInsight MRS HDFS 細粒度鎖優化實踐》,作者:pippo。
背景
HDFS依賴NameNode作為其元資料服務。NameNode將整個名稱空間資訊儲存在記憶體中提供服務。讀取請求(getBlockLocations、listStatus、getFileInfo)等從記憶體中獲取資訊。寫請求(mkdir、create、addBlock)更新記憶體狀態,並將日誌事務寫入到日誌服務(QJM)。
HDFS NameNode的效能決定了整個Hadoop叢集的可擴充套件性。名稱空間效能的改進對於進一步擴充套件Hadoop叢集至關重要。
Apache HDFS 整體架構如下:
Apache HDFS 互動資訊如下:
痛點
HDFS NameNode的寫操作的效能受全域性名稱空間系統鎖的限制。每個寫操作都會獲取鎖並保留鎖,直到該操作執行完成。這樣可以防止寫入操作的併發執行,即使它們是完全獨立的,例如名稱空間中的物件不相交部分。
什麼是Fine Grained Locking(FGL)
FGL【細粒度鎖】的主要目的是通過在獨立名稱空間分割槽上用多個併發鎖替換全域性鎖,允許寫入操作的併發。
當前狀態
HDFS設計思路為一次寫,多次讀。讀操作使用共享鎖,寫操作使用獨佔鎖。由於HDFS NameNode元資料被設計為單個記憶體空間中的名稱空間樹,因此樹的任何級別的寫操作都會阻塞其它寫操作,直到當前寫操作完成。雖然寫是一次,但是當涉及大量併發讀/寫操作時,這就會影響整體效能。
在HDFS NameNode中,記憶體中的元資料有三種不同的資料結構:
- INodeMap: inodeid -> INode
- BlocksMap: blockid -> Blocks
- DataNodeMap: datanodeId -> DataNodeInfo
INodeMap結構中包含inodeid到INode的對映,在整個Namespace目錄樹種存在兩種不同型別的INode資料結構:INodeDirectory和INodeFile。其中INodeDirectory標識的是目錄樹中的目錄,INodeFile標識的是目錄樹中的檔案。
BlocksMap結構中包含blockid到BlockInfo的對映。每一個INodeFile都會包含數量不同的Block,具體數量由檔案大小以及每個Block大小來決定,這些Block按照所在檔案的先後順序組成BlockInfo陣列,BlockInfo維護的是Block的元資料;通過blockid可以快速定位Block。
DataNodeMap結果包含datanodeid到DataNodeInfo的對映。當叢集啟動過程中,通過機架感知逐步建立起整個叢集的機架拓撲結構,一般在NameNode的生命週期內不會發生大變化。
通過INodeMap和BlocksMap共同標識儲存在HDFS中的每個檔案及其塊的資訊。隨著檔案數量的增加,此資料結構大小也會隨之增加,並對單個全域性鎖的效能產生很大影響。下面我們採用簡單的檔案目錄樹結構來演示現有的單一全域性鎖在檔案系統的缺點。
HDFS NameNode 記憶體目錄樹結構
如上圖所示,/D11/D21/D31/F2 和 /D12/D24/D38/F16是不相交的檔案,即有不同的父節點和祖父節點。可以看到F2和F16是兩個獨立的檔案,對其中一個檔案的任何操作都不應該影響另一個檔案。
設計
如前所述,HDFS NameNode將檔案資訊和元資料結構在記憶體中儲存為一個目錄樹結構。當修改任意兩個獨立的檔案時,第二次操作需要等到第一次操作完成並釋放鎖。釋放鎖以後,只有第二個操作獲取鎖後才能繼續修改檔案系統。類似的,後續操作也會阻塞,直到第二次操作釋放鎖。
在下面的例子中,我們考慮2個檔案併發寫入(建立、刪除、追加。。。)操作。F2和F16是檔案系統下的2個獨立檔案(具有不同的父節點和祖父節點)。在將內容追加到F2時,F16也可以同時進行修改。但是由於整個目錄樹全域性物件鎖,對F16的操作必須等對F2的操作完成後才能執行。
代替全域性鎖,可以將鎖分佈在一組名為“分割槽”的檔案中,每個分割槽都可以有自己的鎖。現在F2屬於分割槽-1,F16屬於分割槽-2。F2檔案操作可以通過獲取分割槽-1的鎖來進行修改,F16檔案操作可以通過獲取分割槽-2的鎖來進行修改。
和以前一樣,需要先獲取全域性鎖,然後搜尋每個檔案屬於哪個分割槽。找到分割槽後,獲取分割槽鎖並釋放全域性鎖。因此全域性鎖並不會完全被刪除。相反,通過減少全域性鎖時間跨度,一旦釋放全域性鎖,則其它寫操作可以獲取全域性鎖並繼續獲取分割槽鎖來進行檔案操作。
分割槽的數量如何決定?如果有效的定義分割槽從而獲得更高的吞吐量?
預設情況下,分割槽大小為65K,溢位係數為1.8。一旦分割槽達到溢位條件,將會建立新分割槽並加入到分割槽列表中。理想情況下,可以擁有等於NameNode可用CPU核數的分割槽數,過多的分割槽數量將會使得CPU過載,而過少的分割槽數量無法充分利用CPU。
實現
引入新的資料結構-PartitionedGSet,它儲存名稱空間建立的所有分割槽資訊。PartitionEntry是一個分割槽的物件結構。LatchLock是新引入的鎖,用於控制兩級鎖--頂層鎖和子鎖。
PartitionedGSet
PartitionedGSet是一個兩級層次結構。第一層RangeMap定義了INode的範圍,並將它們對映到相應的分割槽中。分割槽構成了層次結構的第二級,每個分割槽儲存屬於指定範圍的INode資訊。為了根據鍵值查詢INode,需要首先在RangeMap中找到對應鍵值的範圍,然後在對應的RangeSet,使用雜湊值獲取到對應的INode。
HDFS NameNode 兩級層次結構
RangeGSet的容量有一定的閾值。當達到閾值後,將建立新的RangeGSet。空的或者未充分利用的RangeGSet由後臺RangeMonitor守護程式來進行垃圾回收。
HDFS NameNode啟動時,根據映象中的INode數量計算合理的初始分割槽數。同時還需要考慮CPU核數,因為將分割槽數量提高到遠超CPU核數並不會增加系統的並行性。
- 動態分割槽:分割槽的大小有限,可以像平衡樹一樣可以進行分裂和合並。
- 單個分割槽:只有一個分割槽,且只有一個與之相對應的鎖,並且應和全域性鎖類似。這適用於小型叢集或寫入負載比較輕的叢集。
- 靜態分割槽:有一個固定的RangeMap,不新增或者合併現有分割槽。這適用於分割槽均勻增長的檔案系統。而且這將消除鎖定RangeMap的要求,允許並行使用鎖。
Latch Lock
RangeMap與RangeGSet分別有單獨的鎖。Latch Lock是一種鎖模式,其中首先獲取RangeMap的鎖,以查詢與給定INode鍵對應的範圍,然後獲取與分割槽對應的RangeGSet的鎖,同時釋放RangeMap鎖。這樣針對任何其它範圍的下一個操作都可以開始併發執行。
在RangeMap上持有鎖類似於全域性鎖。目錄刪除、重新命名、遞迴建立目錄等幾個操作可能需要鎖定多個RangeGSet。這要確保當前HDFS語義所要求的操作的原子性。例如,如果重新命名將檔案從一個目錄移動到另一個目錄,則必須鎖定包含檔案、源和目標目錄的RangeMap,以便使重新命名成為原子。此鎖定模式的一個理想優化是允許某些操作的Latch Lock與其他操作的全域性鎖結合使用。
INode Keys
HDFS中的每個目錄和檔案都有一個唯一的INode,即使檔案被重新命名或者移動到其它位置,該INode會保持不變。INode鍵是以檔案INode本身結尾,前面包含父INode的固定長度序列。
Key Definition: key(f) = <ppId, pId, selfId>
selfId是檔案的INodeId,pId是父目錄的INodeId,ppId是父目錄的父目錄的INodeId。INode鍵的這種表達不僅保證了同級,同時也保證了表親(相同祖父節點)在大多數情況下被分割槽到相同的範圍中。這些鍵基於INodeId而非檔名,允許簡單的檔案和目錄進行重新命名,稱為就地重新命名,而無需重新進行分割槽。
效果
經過測試驗證使用和不使用FGL功能效能,在主要寫入操作情況下,吞吐量平均提高了25%左右。
詳細效能對比
使用Hadoop NN Benchmarking工具(NNThroughputBenchmark)來驗證NameNode的效能。每個寫入API驗證並觀察到平均25%的效能提升。有很少一部分輕微或者沒有提升的API,分析並發現這些API均是輕量級API,因此沒有太大的提升。
NNThroughputBenchmark是用於NameNode效能基準測試工具。該工具提供了非常基本的API呼叫,比如建立檔案,建立目錄、刪除。在這個基礎上進行了增強,從而能夠支援所有寫入API,並能夠捕獲使用和不使用FGL的版本的效能資料。
用於測試的資料集:執行緒數 1000、檔案數 1000000、每個目錄檔案數 40。
寫入呼叫頻率高的API
其它內部寫API
常用讀取API:
通過完整的FGL實現,讀取API也有很好的效能提升。
執行基準測試工具的命令:
./hadoop org.apache.hadoop.hdfs.server.namenode.NNThroughputBenchmark -fs file:/// -op create -threads 200 -files 1000000 -filesPerDir 40 –close
./hadoop org.apache.hadoop.hdfs.server.namenode.NNThroughputBenchmark -fs hdfs:x.x.x.x:dddd/hacluster -op create -threads 200 -files 1000000 -filesPerDir 40 -close
參考
與FGL相關的社群討論
Hadoop Meetup Jan 2019 — HDFS Scalability and Consistent Reads from Standby Node, which covers Three-Stage Scalability Plan. Slides 21–25
社群中跟蹤與NameNode可擴充套件性相關的其它Jira
HDFS-5453. Support fine grain locking in FSNamesystem
HDFS-5477. Block manager as a service
HDFS-8286. Scaling out the namespace using KV store
HDFS-14703. Namenode Fine Grained Locking (design inspired us to implement it fully)
總結
華為雲FusionInsight MRS雲原生資料湖為政企客戶提供湖倉一體、雲原生的資料湖解決方案,構建一個架構可持續演進的離線、實時、邏輯三種資料湖,支撐政企客戶全量資料的實時分析、離線分析、互動查詢、實時檢索、多模分析、資料倉庫、資料接入和治理等大資料應用場景。
華為雲FusionInsight MRS通過FGL對HDFS NameNode鎖機制進行優化,有效提升了NameNode的讀寫吞吐量,從而能夠支援更多資料,更多業務請求訪問,從而更好的支撐政企客戶高效用數,業務洞見更準,價值兌現更快。
- 什麼是編輯器中的常量傳播?
- Colocate Join :ClickHouse的一種高效能分散式join查詢模型
- Spring Cache設計之美,你品,你細品…
- Python影象處理丨帶你掌握影象幾何變換
- 帶你瞭解TensorFlow pb模型常用處理方法
- 編譯器優化:何為SLP向量化
- 煙霧、空氣質量、溫溼度…自己徒手做個環境檢測裝置
- 不止跑路,拯救誤操作rm -rf /*的小夥兒
- 畢昇編譯器優化:Lazy Code Motion
- 43%非常看好TypeScript…解讀“2022前端開發者現狀報告”
- 華為雲資料治理生產線DataArts,讓“資料‘慧’說話”
- 不需要伺服器,教你僅用30行程式碼搞定實時健康碼識別
- 編譯器工程師眼中的好程式碼:Loop Interchange
- 記憶體問題難定位,那是因為你沒用ASAN
- 大資料開發,Hadoop Spark太重?你試試esProc SPL
- 學python,怎麼能不學習scrapy呢!
- 如何面向物件程式設計?程式設計師:我也要先有“物件”啊
- 一種比讀寫鎖更快的鎖,還不趕緊認識一下
- 實踐GoF的設計模式:迭代器模式
- CWE4.8:2022年危害最大的25種軟體安全問題