董 聰,張 曉,程文迪,石 佳
(1.西北工業大學軟件學院,西安 710129;2.大數據存儲與管理工業和信息化部重點實驗室(西北工業大學),西安 710129;3.西北工業大學計算機學院,西安 710129)
(?通信作者電子郵箱zhangxiao@nwpu.edu.cn)
伴隨著信息化時代的到來,單機的存儲與處理已經無法適應實際應用產生的巨大數據量,因此分布式文件系統的推行成為大數據發展的關注熱點。隨著Hadoop 生態圈的發展,Hadoop 分布式文件系統(Hadoop Distributed File System,HDFS)這個旨在解決大數據分布式存儲問題的分布式文件存儲系統在各行各業的大數據系統中得到了較為廣泛的應用[1]。作為海量數據的存儲底層平臺,HDFS 存儲了海量的結構化和非結構化數據,支撐著復雜查詢分析、交互式分析、詳單查詢、Key-Value讀寫以及迭代計算等豐富的應用場景[2]。
由于上層應用需要向HDFS 寫入或者讀取數據,HDFS 的性能將會影響其上層所有大數據的應用和系統,因此HDFS性能的優化至關重要。在HDFS 性能優化時,既要保持其通用的訪問接口和穩定的性能,也要保證高可用性和高可擴展性等其他特性。針對在大數據背景分析下對HDFS 的存儲和優化,文獻[3]提出了一種異構感知的分層存儲——hatS,它將固態硬盤作為磁盤的上層存儲,把數據副本存儲到不同的層級中。此時文件先在固態驅動器(Solid State Disk,SSD)中存儲,再復制到磁盤中,讀取時優先從最上層讀取。這種放置策略雖然提高了數據寫入的性能,但會造成數據訪問的不均衡。文獻[4]提出了一種根據數據冷熱程度來訪問HDFS 的優化方案。該系統可以遵循一組預定規則,根據熱度使用不同的存儲策略來優化數據的訪問,從而提高整個存儲系統的效率。但該技術目前只適用企業ERP/MES(Enterprise Resource Planning/Manufacturing Execution System)場景,使用具有局限性。
當前新型存儲器件的研究主要集中于低延遲存儲與持久內存,但隨著成本的降低,新型存儲器件也開始應用于大規模分布式文件系統以提高性能。受到廣泛關注的非易失內存(Non-Volatile Memory,NVM)類似于動態隨機存取存儲器(Dynamic Random Access Memory,DRAM),是按字節尋址,提供了對持久性存儲的快速、細粒度訪問,并保證不會在斷電后丟失數據,因此被認為是一種具有廣泛前景的新型存儲[5]。目前新型存儲器主要有3 種:相變存儲器PCM(Pulse Code Modulation),以英特爾與美光聯合研發的3D Xpoint 為代表;磁變存儲器MRAM(Magnetic Random Access Memory),以美國Everspin 公司推出的STT-MRAM 為代表;阻變存儲器ReRAM(Resistive Random-Access Memory),目前暫無商用產品,代表公司有美國Crossbar。
由于硬件與軟件接口限制了NVM 設備硬件優勢的發揮,國內外研究根據NVM 的存儲特性設計并改良傳統的操作系統和數據管理方式。文獻[6]提出了Tinca,一種事務NVM 磁盤緩存的新機制,它以COW 方式將數據保存到非易失性主內存(Non-Volatile Main Memory,NVMM)緩存中,并可以在日志數據和文件數據間切換,避免雙重寫入;但Tinca 的設計被緊密耦合在Ext4(fourth Extended filesystem)的體系結構中,這種基于慢速存儲設備的存儲方式并不適用于NVM。文獻[7]提出了HiNFS,該文件系統操作以細粒度的方式將緩沖和直接訪問結合起來,旨在通過DRAM 緩存延遲的持久寫入操作來隱藏NVMM 的高寫入延遲,從而形成DRAM-NVMM 混合架構;但這種方式未考慮DRAM高速緩存的崩潰一致性問題。
由于非易失性內存產品比磁盤和固態硬盤的讀寫性能更高,同時還能持久化地存儲數據[8],因此,最近幾年開始出現將NVM集成到HDFS中的研究。文獻[9]提出了NVFS(NVMand RDMA-aware HDFS),在現有的HDFS 中加入了對NVM 和遠程直接數據存取(Remote Direct Memory Access,RDMA)的支持。NVFS中NVM 提供兩種訪問模式:塊訪問和內存訪問。塊訪問模式下,NVM 可以作為塊設備被加載到DataNode 的本地文件系統中,文件系統的I/O 操作調用NVMe(Non-Volatile Memory express)存儲系統接口通過底層的NVMe 驅動實現。內存訪問模式下,DataNode 的讀寫線程可以通過NVM 的直接內存訪問接口按照內存語義訪問NVM。且針對上層應用,NVFS 將預寫日志(Write-Ahead Logging,WAL)存儲在NVM中,其他數據存儲在SSD 中。這在一定程度上提升了HDFS的性能,但該方案目前只針對于特定的上層應用,不具有通用性,因此還需要繼續研究。本文針對通用的基于NVM 的HDFS重新設計其寫入流程,以此來提高性能和獲得上層應用的通用性。總之,HDFS 在實際生產系統中應用廣泛,對其上層應用的性能有很大影響。雖然NVM 這類新型存儲器件可用于HDFS 的性能提升,但是目前較多用于本地文件系統,而關于分布式系統的研究則較少。因此本文提出了基于NVM的HDFS寫性能優化方案。
新一代的非易失性存儲介質NVM 和DRAM 性能相近,但其容量更大、價格更低[10]。NVM 主要包括兩大類:基于外存塊尋址的NVM(如閃存)擁有性能高、能耗低等優勢,已經被廣泛應用于高性能存儲系統中;基于內存的NVM 即持久性內存,其具備快速持久化與可字節尋址的特征,以及具有與內存相似的讀性能和掉電非易失的特性,它的出現為大規模存儲系統的改進提供了新的思路和方向。
2017年10月,Intel發布了一款名為Optane SSD 900P的固態硬盤[11],該設備采用了非易失性存儲技術3D XPoint,將NVMe 與非易失性存儲技術結合,被稱為世界上最快、可用性最好的一款專業級SSD[12]。這里使用Linux中通用的性能測試基準fio 對NVM_DISK(模擬NVM)、Optane 900P SSD(480 GB)、SAMSUNG SATA-SSD 860 EVO(250 GB)[13]的讀寫吞吐量進行測試與對比。fio是一個開源I/O壓力測試工具,提供了豐富的存儲引擎支持和豐富的參數類型,被廣泛應用于存儲系統的性能測試。fio 提供了基準測試工具,可以測試不同設備在不同塊大小、不同讀寫模式下的帶寬、每秒進行讀寫操作的次數(Input/Output operations Per Second,IOPS)和延遲,結果如表1 所示。通過表1 測試結果可以看出,Optane-SSD 與SAMSUNG SATA-SSD相比,4 KB隨機讀取的讀取IOPS及帶寬均提升為原來的8 倍,讀延遲減少為原來的1/8.7,但Optane-SSD與模擬NVM相比,4 KB隨機讀取的讀取IOPS及帶寬均下降為原來的1/2.8,讀延遲增加為原來的4.3 倍。Optane-SSD與SAMSUNG SATA-SSD相比,4 KB隨機寫入的IOPS及帶寬均提升為原來的9.2 倍,寫入延遲減少為原來的1/9.7。且Optane-SSD與模擬NVM相比,4 KB隨機寫入的IOPS及帶寬均提升為原來的2.7倍,寫入延遲減少為原來的1/1.8。
HDFS 是由多臺機器組成的一個邏輯上的應用級文件系統,通過庫調用的方式提供數據存儲服務,將數據分散存儲在集群中的多臺機器上,其上運行著相關的存儲軟件,文件系統對外提供了統一的文件存儲服務,因此需要綜合考慮硬件故障、網絡故障、擴展性以及可用性等多重因素。HDFS 通過將文件劃分為固定大小的塊進行存儲,客戶端首先會與NameNode通信得到塊存儲的節點地址列表,然后將數據塊以文件的形式存儲在DataNode 節點。HDFS 的DataNode 節點用于存儲文件的數據塊,每個數據塊以單獨文件的形式保存在DataNode 節點的本地文件系統上。因此,各個DataNode 節點中的存儲介質可掛載為不同類型的本地文件系統[14]。
如圖1 所示,將HDFS 根據其組織方式由下至上分為5層,分別是存儲介質層、文件系統層、軟件調用層、分布式結構層以及應用層。因為要保持HDFS 本身接口的兼容性,所以文件系統的改進應盡可能與舊版本的接口與功能兼容。因此,在維護本地文件系統的POXIS(Portable Operating System Interface of UNIX)規范的同時,替換底層存儲介質是最友好的NVM 設備加入方式。這無需對代碼邏輯作出更改,且HDFS提供數據存儲目錄中存儲設備的配置方法。首先,在Linux 5.0 內核版本下通過內存模擬的方式模擬持久性內存,并為其掛載不同類型的文件系統。然后對HDFS 的存儲介質層進行改動,將NVMe-SSD與模擬的持久性內存加入到HDFS中替換傳統的硬盤驅動器(Hard Disk Drive,HDD)設備,并為HDFS 集群的所有DataNode 節點替換并配置不同的存儲介質與文件系統,分別用NVMe-SSD-ext4、PM-ext4-dax、PM-NOVA、HDD 文件系統作為DataNode 端的存儲設備。最后,通過在HDFS DataNode 的寫入路徑中添加時間戳的方式,得到了一個固定大小的塊(128 MB)的寫入磁盤所用時間與其他處理操作所用時間,并對各種存儲介質的表現進行了對比。

圖1 HDFS分層存儲結構Fig.1 HDFS hierarchical storage structure
測試結果如圖2所示,根據實驗結果發現NVMe-SSD等快速持久化設備加入HDFS 集群并替換HDD 作為DataNode 的外部存儲,性能有一定的提升。該方式并未改變數據的寫入方式,其寫入耗時的占比雖然有所減小,但整體性能提升效果并不明顯。而將持久性內存以塊設備的形式掛載并配置到HDFS 的寫入路徑中,HDFS 中寫入耗時的占比由32%下降至28%,將其掛載為NOVA[15]文件系統,其寫入耗時的占比由31%下降至20%。由此可見,NVM 設備的加入可以在一定程度上提升文件的寫入性能,且使用適配于NVM 設備的ext4-dax 與NOVA 文件系統,獲得的提升幅度更大。由1.1 節的測試結果可知,NVMe-SSD 與SATA-SSD 相比,二者的裸盤性能最多相差9 倍多。而對HDFS 的寫入測試數據顯示,NVMe-SSD 的加入并未獲得顯著的性能提升,并且將持久性內存加入HDFS 中,最多也只能將塊數據寫入耗時從原來的32%減少到20%。因此,通過簡單的更換數據節點的存儲介質,并未充分發揮NVMe-SSD 與NVM 設備優異的硬件性能,對于集群的性能提升幅度不太理想。

圖2 HDFS不同存儲介質處理和寫入耗時占比Fig.2 Time consumption proportion of processing and writing of different HDFS storage medias
通過分析發現僅替換其底層存儲介質的方式,雖然保留了HDFS 的原始讀寫方式,且配置方法與設備的更換較易操作,但NVM 自身的硬件優勢被分布式環境中數據的處理與傳輸過程覆蓋,并未獲得較大的性能提升。因此,要對HDFS 的寫路徑進行詳細的分析,在軟件調用層詳細探究HDFS 的軟件調用過程中存在的性能瓶頸。
為了更好地優化HDFS,首先要了解HDFS 的集群架構與副本放置策略。HDFS采用Mater/Slave的架構來存儲數據,主要由四個部分組成:Client、NameNode、DataNode 以及Secondary NameNode[16]。如圖3 所示,Client 節點代表用戶通過HDFS 提供的類POSIX 接口,通過與NameNode 和DataNode交互來訪問文件系統中的數據;NameNode管理著集群中的元數據,處理客戶端的請求,其中元數據主要包括文件系統命名空間和數據塊的存儲位置等;DataNode 管理集群中客戶端發來的讀寫請求,且負責管理節點本地文件系統中數據塊,并定時向NameNode 發送心跳,反饋本節點狀態。同時為了提供存儲的容錯性,每個數據塊都存有一定數量的副本,用戶可以自行設置(默認為3 副本)。所有的數據塊由NameNode 決定其存儲的位置;Secondary NameNode 主要作用是定期合并NameNode 中的元數據文件,在NameNode 發生故障時可以作為輔助節點恢復NameNode中的數據。
為了保證可靠性,HDFS 采用了多副本機制,如圖3所示,數字方塊代表數據塊,相同數字的方塊表示同一數據塊及其副本。在副本數為3的情況下,HDFS 的默認副本放置策略將3 個副本中的2 個放在同一機架(Rack)中的2 個不同DataNode 上,另外1 個副本放置在不同機架的DataNode 上。如果HDFS 集群跨越多個數據中心,還可以配置更多的副本數,并調整副本放置策略保證至少1 個副本存儲在不同的數據中心。這樣的副本放置策略在容錯和寫入性能方面進行了有效的權衡:一方面保證了數據副本在多節點、多機架甚至多數據中心的冗余,提高了系統容錯能力;另一方面,保證盡可能多的副本優先寫入到網絡距離較近的節點(如同一節點、機架或數據中心)中,由于網絡距離較近的節點間網絡帶寬較高、傳輸延遲較低,保證了HDFS Client向HDFS中寫入數據塊的性能。

圖3 HDFS集群架構和數據塊放置策略Fig.3 Cluster architecture and data block placement strategy of HDFS
HDFS 文件系統以塊(默認128 MB)為基本單位進行文件的寫入,文件按照固定塊大小分割為若干個塊存儲在DataNode 上。集群通過在客戶端、NameNode、DataNode 各節點間建立pipeline進行文件的傳輸,每個塊在傳輸過程被拆分為固定大小的packet(默認64 KB)[17],packet 由chunk 組成且chunk 是客戶端向DataNode 或DataNode 間的pipeline 中數據傳輸與校驗的基本單位。以packet 為單位,依次將數據從客戶端寫入DataNode 節點。HDFS 設置多副本冗余存儲策略且采用流式接口實現對數據的訪問。
如圖4 所示,首先客戶端將packet 傳入DataNode 中,DataNode會盡量一次接收一個完整的packet,并把這個packet全部存放到緩存塊中,然后一邊把接收到的packet 寫入本地磁盤,一邊把它發往下一個接收端,最后在pipeline 中的最后一個DataNode 節點上驗證數據,并返回確認幀確認這個packet已經完成。

圖4 HDFS寫入通信流程Fig.4 Flowchart of HDFS writing communication
HDFS 文件的寫入包括客戶端、DataNode 與NameNode 三類節點。客戶端中主要包括DistributedFileSystem、DFSClient、DFSOutputStream、DataStreamer 等類;DataNode 端包括DataNode、DataXceiverServer、DataXceiver、BlockReceiver等類;NameNode 中包括NameNode、NameNodeRpcServer 等類。如圖5 所示,將HDFS 寫入文件的過程分為建立連接、數據處理與存儲,以及斷開連接3個階段:
1)首先,客戶端調用DistributedFileSystem 對象的create()方法向NameNode 發送通信請求。隨后,客戶端創建FSDataOutputStream 對象進行文件傳輸。若NameNode節點驗證并通過客戶端的寫入請求,則會在NameNode 節點中創建一個文件條目(Entry),然后獲取塊文件存儲節點信息,最后將寫入的節點信息返回至客戶端。
2)首先,客戶端將從NameNode 返回的節點列表構造一個pipeline(即管道)。然后,客戶端會和pipeline 中的第一個節點(即主DataNode)進行通信并建立連接。客戶端將準備寫入的數據分為多個packet,存儲到一個FSDataOutputStream 對象的內存緩沖區中。客戶端通過FSDataOutputStream 對象,向pipeline 中的主DataNode 節點發送數據。packet 數據在pipeline 中的DataNode(默認為3)依次向下游節點按序傳輸。在發送數據相反方向,pipeline中的各個DataNode節點不斷地向上游節點發送寫入完成的ack響應。pipeline中的節點重復執行此過程直至處理完成當前塊的最后一個packet。最終由pipeline 中的第一個DataNode 節點將最后一個packet 的寫入完成響應ack,發送給客戶端。
3)首先,客戶端通過調用FSDataOutputStream 對象的close()方法,關閉客戶端與DataNode 之間的輸出流。然后,客戶端調用DistributedFileSystem 對象的complete()方法,向元數據存儲節點通知文件的寫入已完成。最后,客戶端向NameNode節點發送寫入完成信息,關閉所有的連接。

圖5 HDFS數據塊寫入路徑Fig.5 HDFS data block write path
假設文件被分為M個塊,設寫入過程中階段1 建立連接用時為T1,階段2數據處理與存儲用時為T2,階段3斷開連接用時為T3。階段1 所用時間(T1)為客戶端與NameNode 通信時間以及NameNode 獲取節點列表的時間,可表示為常數tcn。階段2 所用時間(T2)包含客戶端與DataNode 通信時間tcm(建立連接過程與創建pipeline 的過程)、主DataNode 接收客戶端傳輸數據的時間ttransi、主DataNode 將數據持久化到磁盤的時間、主DataNode 將數據發送至下游節點的時間、主DataNode等待packet到達響應的時間。階段3為關閉連接的所用時間(T3),表示為常數tclose。則寫入一個文件的用時T如式(1)所示:

通過上述寫過程可以分析得出,HDFS的讀寫性能主要受4個因素的影響:
1)數據塊的數量,即文件的大小。
2)節點之間的通信速度,包括Client 與NameNode、Client與DataNode、DataNode 與DataNode 之間的網絡傳輸速度,這取決于節點間的網絡狀況。
3)寫入/讀取過程中,DataNode磁盤的讀取/寫入速度。
4)從NameNode 獲取塊數據存儲節點列表時,NameNode的元數據查詢速度。
將文件的寫入分為3 個階段并通過log 統計分析,各階段時間占比如圖6 所示,可以得到對于文件中的某一數據(128 MB)的寫入,DataNode 間pipeline 中數據的傳輸時間與處理過程占整個block寫入過程的85%左右,因此消耗時間最多的步驟就是文件寫入過程中的主要瓶頸。
因此,本文針對如何優化DataNode 間pipeline 中數據的傳輸時間以及處理過程提出兩種優化策略,將在第3 章繼續分析寫入階段中各階段的時間占比。

圖6 HDFS數據塊各階段寫入時間Fig.6 HDFS data block write times at different stages
HDFS 通常用來處理TB 甚至PB 級別的數據。但由于文件大小的增加,數據在客戶端與DataNode 節點之間傳輸與處理的耗時占整個寫入過程的百分比就越大。通過第2 章的研究與分析可知,文件的寫入過程主要瓶頸存在于DataNode 節點間,即pipeline 的寫入過程。pipeline 中塊數據的寫入按時序可分為如下4個階段:
1)客戶端輸入流傳入的數據保存在內存buffer 中,包括packetHeader信息、校驗數據、實際數據等。
2)當前packet 數據發送到下游DataNode(非pipeline 中的最后一個節點)。
3)若為pipeline中最后一個節點,則對數據進行校驗。持久化校驗和數據及packet數據到文件輸出流中。
4)對packet 數據的處理與計算過程。將packet 信息加入PacketResponder 線程隊列中,等待下游節點的響應,并更新packet寫入信息。
將上述4 個過程的耗時分別稱為receive time、mirror time、write time 及process time。如圖7 所示,通過測量得到了packet 數據的寫入過程中4 個階段的平均耗時以及4 個階段的耗時占比。由測量結果可知,receive time 即接收數據的耗時占40~50%,是寫入過程中最耗時的操作。
將上述4個階段的用時receive time、mirror time、write time及process time 分別稱為T0、T1、T2、T3,其耗時占比為43∶21∶31∶5。pipeline中各DataNode節點的數據處理流程如下:
1)接收處理packet0到內存中并將其寫入到DataNode1。
2)由DataNode1負責將packet0傳輸到DataNode2,同時客戶端可以開始接收處理packet1 并將其寫入到輸出流,DataNode1處理完packet0后便開始接收packet1。
3)同時DataNode2負責將packet0寫入DataNode3。
4)重復上述3 個步驟將多個packet 依次傳輸至主DataNode 中,直至所有的packet 數據傳輸并持久化完成后寫入結束。

圖7 packet寫入過程各階段耗時占比Fig.7 Time consumption proportion of packet at different stages of writing process
按照原始的寫入流程,客戶端向DataNode 寫入一個文件的總時間取決于packet 的數量與單個packet 的處理時間。即總體寫入時間與packet 數量和單個packet 的處理時間均成正比,因此,想要降低總體的處理時間,有兩種可行的方案:
1)增大packet大小。
如圖8 所示,HDFS 中一個固定大小的塊(如128 MB)由多個固定大小的packet 組成,packet 數據是由多個chunk 組成,單個packet 中包含header(packet 的概要屬性)、循環冗余校驗(Cyclic Redundancy Check,CRC)和數據以及其對應的數據。綜上,通過增大packet 的大小可以直觀地減少packet 的數量。

圖8 packet數據結構Fig.8 packet data structure
如圖9 所示,在第4.1 節所述的測試環境下,通過TestDFSIO 測試了將packet大小更改為128 KB 與256 KB 后的集群寫入性能,發現增大packet性能最大可提升11.1%,這是由于數據傳輸過程減小的時間多于單個pacekt處理增加的時間,因此總體時間變短。但由于單個packet 的處理時間也與packet 的大小相關,當packet 設置過大,單個packet 的處理時間也會隨之增加,并且相同的網絡條件下,傳輸大的數據相較傳輸小的數據更加容易出錯,給網絡傳輸帶來較大的壓力,因此這種方式并不是最為理想的優化方案。
2)減少單個packet的處理時間。
假設單個packet 的處理時間為t,一個塊中共有N個packet,若將t減少到50%,則總時間T也會由N×t減小至50%×N×t。因此該方式可以有效地減少寫入的總處理時間,從而性能得到較大的提升。如圖10 所示,基于分析,在HDFS 原有設計的基礎上,對客戶端到DataNode 的pipeline 數據傳輸的過程進行改進。通過改進了寫入流程的關鍵路徑,分離了pipeline 寫入數據的4 個階段。單獨處理從輸入流接收數據到buffer 這一過程。為單個DataNode 中的每一個塊的寫入過程分配了(64 KB×100=6.25 MB)的內存空間,作為存儲packet數據的臨時緩沖區。
在原始的HDFS 設計中,相鄰packet 之間的寫入耗時較長,下一個packet 的處理需要等待上一個packet 接收完成、發送到下游節點并持久化完成后,才可以開始從輸入流接收下一個packet。該設計會使得數據塊的寫入時間受到網絡傳輸速度與磁盤I/O 的影響,因此本文為packet 創建了臨時緩沖區,用于保存從輸入流中接收的packet 數據。這時packet 數據的處理流程(包括向下游節點發送數據、校驗并持久化、計算及其他操作)可以與packet數據的接收異步進行,而下一個packet可以更快地被接收與處理。

圖9 更改packet大小后HDFS寫入吞吐量對比Fig.9 HDFS write throughput comparison after changing packet size

圖10 修改前后packet數據處理流程對比Fig.10 Comparison of packet data processing flow before and after modification
如圖10所示,這時DataNode 端寫入數據的整體處理時間T由式(2)變為式(3)。另外,當數據塊數量較大時,數據傳輸時間t位于(N×T0,(N+1)×T0)。

要減少單個packet 的處理時間,需要在Hadoop 2.9.0 版本中的DataNode 端加入相應的寫緩沖處理機制。下面將整個優化流程分為兩個處理實現模塊,并設計具體的實現細節。
3.2.1 寫緩沖優化機制的實現
DataNode端的塊數據接收和處理流程可以看作是一個典型的生產者-消費者的問題。DataNode 端的packetReceiver 類中的receivePacket(in)方法作為生產者,從客戶端/上游DataNode 發送的輸入流in 中不斷接收packet 數據,而PacketResponder 線程作為消費者,負責接收下游節點的ack響應,并向上游節點發送已處理完成的ack 響應。ack 信息則為生產與消費的對象。因此,當pipeline 中的主DataNode 接收到塊中最后一個packet 的ack 響應后,就代表所有的packet數據均已成功寫入所有的副本中。通過生產者-消費者作為切入點,具體實現DataNode端寫流程的優化。
1)異步阻塞隊列的加入。
Java 為開發者提供了幾種常用的阻塞隊列,如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlocking-Queue 等。阻塞隊列中維護了一個連續的存儲空間,生產者線程不斷地向隊列中放入數據對象直至隊列滿。消費者線程不斷地從隊列中取出數據對象直至隊列為空。當隊列滿時,生產者線程會以阻塞的形式等待消費者線程的消費數據。當隊列為空時,消費者線程會以阻塞的形式等待生產者線程生產數據[18]。LinkedBlockingQueue 是Java自帶的一種基于鏈表的有界阻塞隊列,在大量數據并發操作時,可以通過其內部的兩個讀寫鎖進行異步的讀寫操作而不相互影響。
為了實現寫緩沖優化,在DataNode 端接收到packet 數據后,需要將數據先暫存在分配的緩沖區中,然后再進行異步的持久化與處理操作。隊列結構是一種先進先出的數據結構,使用隊列來存儲數據不僅可以保證packet 數據的按序輸出,同時可以實現對packet 的暫存。因此,為了使得生產者與消費者之間相互獨立地進行讀寫操作,既可以實現在客戶端向隊列中寫入packet數據(生產者),同時packetProcesser線程可以進行packet 數據的處理(消費者),通過使用LinkedBlockingQueue 這一基于鏈表實現的異步阻塞隊列完成了對packet數據的生產、消費流程。
如圖11所示,對于DataNode2來說,從上游節點的輸出流in 中接收發送的packet 數據,同時也通過mirrorout 向下游節點DataNode3 發送packet 數據,從上游節點傳輸得到的packet數據被依次保存在異步處理隊列processQueue 中。packetReceiver 則負責將數據不斷地置入隊列(即生產過程),而packetProcesser線程則負責packet數據的處理,包括發送至下游節點、校驗與持久化、計算與其他處理(即消費的過程)。在整個塊數據寫入的過程中,維持了一個穩定的生產-消費packet 數據的過程。通過實驗驗證,為每一個數據塊分配大小為100 的packet 緩沖區,可以滿足數據傳輸速度與數據處理速度之間的平衡。

圖11 HDFS pipeline輸入輸出流Fig.11 HDFS pipeline input and output stream
2)異步處理線程。
如圖12 所示,為了在DataNode 端加入寫入緩沖機制,需要在DataNode 端構造新的線程去處理保存在阻塞隊列中的packet 數據,將保存在緩沖buffer 中的packet 數據按序進行分發、校驗以及持久化等操作。

圖12 HDFS寫緩沖異步處理機制的實現Fig.12 Implementation of HDFS write buffer asynchronous processing mechanism
對于DataNode 來說,當緩沖buffer 中接收到packet 數據后,即可進行下一個packet 的接收。這種異步處理流程無需進行后續的計算、校驗以及持久化到磁盤等過程,經過實驗分析,這種方式減少了DataNode 節點寫入數據塊的總時間,并將單個packet的接收與處理時間重疊。詳細步驟如下:
首先,由于線程對象的創建與銷毀對資源的消耗較大,為了節省計算資源、提升存儲空間利用率以及提高線程的可管理性,在DataNode 端創建了newCachedThreadPool 可緩存線程池來管理新創建的線程,主要有兩個數據處理線程(packetPersister 與packetProcesser)與一個響應處理線程(packetResponder)。
1)packetPersister 線程。在開始傳輸塊數據前創建并啟動線程,對于保存在內存buffer 中的packet 數據,在經過前面的計算、校驗等處理流程后,將其以先進先出的順序暫存于阻塞隊列中,然后依次進行持久化操作。packetPersister 線程需要等待最后一個packet處理完成才可關閉。
2)packetProcesser線程。在開始傳輸塊數據前,創建并啟動線程,將buffer中接收到的packet數據存儲依次向下游節點發送。packetProcesser 線程在最后一個packet 處理完成后才可關閉。
3)packetResponder 線程。要減少單個packet 的處理延時,則需要修改packetResponder 的處理流程。通常認為當packet 被持久化至緩沖buffer 后,就表示packet 數據已經完成持久性。因此可以向packetResponder 線程發送寫入成功響應。此時單個packet的接收、處理延時減少。對于pipeline 中的非末尾DataNode 節點都只需進行packet 的接收,即可得到返回的響應。而對于pipeline中的最后一個節點,需要對數據進行校驗并持久化后才能向上游節點返回成功的響應。
在HDFS中加入異步阻塞隊列處理核心代碼如下所述:


3.2.2 數據異常處理方案
3.2.1 節提出的HDFS 寫緩沖優化方案通過更改塊數據的寫入流程,使性能得到一定程度上的提升,但同時也引發了一些可靠性問題,下面討論了寫流程的更改是否仍然能夠保證節點間數據的一致性及集群中數據的可靠性。
首先,針對在塊數據的pipeline 寫入流程中DataNode 端可能出現的幾種異常情況進行了如下分析:
1)DataNode 節點接收到的packet數據的頭部信息出現亂序或長度為負值。這一般是由于網絡異常等原因,導致前面傳輸時packet 信息丟失或packet 的頭部信息讀取錯誤,接收到的數據不可用。此時已無法保證先前packet信息的一致性與正確性,應中斷packet 數據的寫入,建立pipeline 重新進行傳輸。
2)packet 數據在傳輸到下游節點的過程中出錯,下游節點無法接收到已發送的數據。這是由于I/O 流出現錯誤或者節點間網絡出現了異常情況,導致數據的分發時間過長,無法保證數據成功發送至下游節點,則代表pipeline 已經出現問題,無法為數據提供正常的傳輸服務。這時出現錯誤的DataNode 節點應捕獲分發數據時發生的異常信息,判斷是否為異常中斷情況。若為I/O異常等所帶來的中斷情況,則拋出I/O 異常;若非中斷情況,則等待線程的run()方法繼續執行,標記mirrorError位為true,然后DataNode會向客戶端節點告知異常并等待客戶端的處理,而客戶端會根據情況關閉pipeline。
3)packet 數據在校驗時出現錯誤。若通過CRC 確認packet 數據與校驗和數據無法對應,校驗過程失敗,即說明發生了數據異常。此時委托packetResponder 線程返回ERROR_CHECKSUM 的響應ack,然后拋出校驗和異常,中斷寫入過程。
4)packet 數據在持久化到磁盤時出錯。通常,從上游節點傳入的packet的開始偏移信息與磁盤上的現存數據是對齊的。若packet 的開始偏移量與校驗和無法對齊,則可能會出現數據丟失/錯誤失效的情況。當涉及到故障恢復時,客戶端的狀態與DataNode 節點的數據會出現不一致的情況,即客戶端發送的數據并未真正地寫入到DataNode 節點中。這時,客戶端可能會重新發送部分已經在磁盤上的數據。在數據寫入校驗和緩沖區到磁盤時,應跳過已正確寫入的數據。
本節對寫入過程中DataNode 端的異常處理情況進行了介紹,但大多數情況下,是由客戶端節點對塊寫入過程中的異常進行進一步處理的:Client 通常會根據DataNode 端發送的異常響應進行相應的處理(如失敗重傳、宕機后重啟節點等)。如圖13 所示,Client 節點中的DataStreamer 線程負責數據的傳輸。DataStreamer 從dataQueue(待發送packet 數據列表)中獲取packet 數據,然后將其發送至主DataNode,并將packet 數據依次放入ackQueue(packet 數據響應列表)中。對于每一個packet數據,只有當pipeline 中的所有DataNode 都寫入成功并成功返回響應,才表示這個packet數據已寫入成功。
若在寫入過程中Client 節點得到了錯誤的響應,則客戶端會將所有未成功寫入的packet 數據從ackQueue 中依次移除。再通過重新建立新的pipeline 連接,將異常的DataNode節點從pipeline 中移除。然后,開始從dataQueue 中重新發送數據。
此外,DataNode 需要每隔一段時間(默認為3 s,通過dfs.heartbeat.interval設置)向NameNode 發送心跳信息,并且NameNode也會通過心跳向DataNode下發命令。若NameNode很久(默認為5 min,通過heartbeat.recheck.interval設置)無法接收到DataNode 的心跳信息,則NameNode 會將該DataNode認定為失效(HDFS中設置的默認超時時間是10 min+30 s)。

圖13 Client端數據處理流程Fig.13 Flowchart of Client-side data processing
同時,NameNode將缺失的副本根據副本因子數再復制一份。其中,DataNode 節點被認定失效的時間timeout的計算公式如式(4)所示:

首先,選用通用的文件系統性能測試基準如TestDFSIO對本文優化方案進行測試與分析。然后,結合實現方案、集群特征與硬件特征,通過設計一系列實驗對本文所述優化方案的效果進行了評估、驗證與對比,并在多個維度對實驗結果進行了詳細分析。
本次實驗對Hadoop 2.9.0 版本進行比對實驗,集群共配置5個節點,包括1個NameNode節點,3個DataNode節點,1個Client 節點。有關Hadoop 集群各節點的配置信息如下所示:CPU 為Intel Xeon Gold 5115 CPU 2.40 GHz;網絡為Intel 10 Gigabit PCI Express Network Driver(Speed 為10 000 Mb/s);內核版本為GNU/Linux 4.15.0-43-generic x86_64;內存為32 GB;硬盤大小為1.8 TB-HDD、240 GB-SATA-SSD、480 GBNVMe-SSD;總核數為40;操作系統為Ubuntu 18.04.3 LTS;JDK版本為1.8.0_11-b12。
實驗使用通用測試基準TestDFSIO 工具對大數據存儲系統Hadoop 進行測試與分析,得到分布式文件系統HDFS 的讀寫性能指標。
TestDFSIO 是Hadoop 集群自帶的一個benchmark 測試程序,該基準通過調用集群自帶的MapReduce 任務來并發執行讀寫操作。其中,Map任務用于讀寫文件中的數據,Reduce任務則用于記錄累計的統計信息[19]。TestDFSIO 的測試結果取決于HDFS 的讀寫性能。測試會產生兩個最重要的性能衡量指標:每個Map 任務的平均吞吐量(單位為MB/s);單個文件寫入I/O 的平均速度(單位也為MB/s)。此外,使用TestDFSIO進行測試讀寫性能需要清空linux 文件系統緩存,并在測試結束后使用自帶的clean 命令清理文件。本文通過測試不同文件大小的吞吐量與I/O 平均速度等指標,對HDFS 集群的讀寫性能進行評估。
同時還設計了直觀的HDFS 文件讀寫基準,用于測試集群的讀寫性能,將其稱為Test_Execution_Time,其主要思想為:使用HDFS 的客戶端文件系統命令(put、get)向集群中寫入或讀取N個固定大小的文件,通過統計整個過程的寫入時間或讀取時間,可以得到集群寫入/讀取單個大小為M的文件的執行時間。然后,通過對比不同方案下文件的寫入時間,測試出集群的I/O性能是否提升。
本節通過使用常用的兩種測試工具TestDFSIO 與Test_Execution_Time,分別測試了原始HDFS 集群與寫緩沖優化后HDFS的寫入性能。將原始的HDFS稱為HDFS-org,將基于NVM 的寫緩沖優化后的HDFS 定義為HDFS-mod,其測試結果如圖14~16所示。

圖14 TestDFSIO測試HDFS寫入吞吐量對比Fig.14 Comparison of HDFS write throughput tested by TestDFSIO

圖16 Test_Execution_Time測試的HDFS寫入執行時間對比Fig.16 Comparison of HDFS write execution time tested by Test_Execution_Time
首先,使用測試基準TestDFSIO 對集群的寫入性能進行測試,分別測試了100 個50~500 MB 大小的文件,向集群中寫入了約5~50 GB 的數據。從圖14~15 可以觀察到,集群的寫入吞吐量與吞吐率均得到了15%~24%的性能提升。這表明在HDFS中加入寫入緩沖區,并且改進HDFS寫入路徑處理流程,集群的寫入吞吐量與吞吐率均可以得到明顯的性能提升。
如圖16 所示,還使用了Test_Execution_Time 基準測試了向集群中寫入100 MB、200 MB、500 MB、1 GB、2 GB 文件的寫入執行時間。可以看到,隨著寫入文件大小的增加,所需時間也在不斷增加。而通過寫緩沖優化的HDFS 與原始的HDFS相比,執行時間最高減少了36.1%,平均減少了28.1%,在很大程度上提升了集群的寫入速度。
本文研究了基于非易失性存儲器件NVM 的大規模分布式存儲系統的性能優化方案,核心研究對象是Hadoop 分布式文件系統(HDFS)。通過分析新型存儲器件的優勢和HDFS的讀寫路徑時序及其方法間的調用關系,本文指出了新型存儲器件使用面臨的挑戰和HDFS 的讀寫瓶頸,設計并實現了一種數據的異步寫入方案。該方案主要在DataNode 端加入寫入緩沖區,并對HDFS 的pipeline 寫入流程進行了并行化處理。通過實驗測試可知,此方案將HDFS 的寫入性能最高提升了36%。
本文研究還存在一些不足:因市場上現有的NVM 設備少,本文研究中大部分實驗是以內存模擬的方式進行的,而NVM 設備在實際應用過程中可能出現讀寫不對稱、壽命有限、磨損均衡等問題,所以測量出的性能提升與理想狀態有一定的差距,未來可進行進一步的完善。