基於 NVMe SSD 的分佈式文件存儲 UFS 性能提升技術解析

UFS (UCloud File System) 是一款 UCloud 自主研發的分佈式文件存儲產品,此前已推出容量型 UFS 版本。UFS 以其彈性在線擴容、穩定可靠的特點,爲衆多公有云、物理雲、託管雲用戶提供共享存儲方案,單文件系統存儲容量可達百 PB 級。

爲了應對 IO 性能要求很高的數據分析、AI 訓練、高性能站點等場景,UFS 團隊又推出了一款基於 NVMe SSD 介質的性能型 UFS,以滿足高 IO 場景下業務對共享存儲的需求。性能型 UFS 的 4K 隨機寫的延遲能保持在 10ms 以下,4K 隨機讀延遲在 5ms 以下。

性能的提升不僅僅是因爲存儲介質的升級,更有架構層面的改進,本文將從協議、索引、存儲設計等幾方面來詳細介紹性能型 UFS 升級改造的技術細節。

協議改進

此前容量型 UFS 設計時支持的協議爲 NFSv3,其設計理念是接口無狀態,故障恢復的邏輯簡單。此外 NFSv3 在 Linux 和 Windows 上被廣泛支持,更易於跨平臺使用。但是 NFSv3 的設計缺點導致的高延遲在高 IO 場景下是不可接受的,所以在性能型 UFS 中,我們選擇僅支持性能更好、設計更先進的 NFSv4 協議。

NFSv4 與 NFSv3 相比,更先進的特性包括:支持有狀態的 lock 語義、多協議間的 compound 機制等。特別是 compound 機制,可以讓多次 NFS 協議交互在一個 RTT 中完成,很好地解決了 NFSv3 性能低效的問題。一次典型的 open for write 操作,在 NFSv3 和 NFSv4 上分別是這樣的:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

可以看到,在關鍵的 IO 部分,NFSv4 比 NFSv3 節省一半的交互次數,可以顯著降低 IO 延遲。除了協議以外,性能型 UFS 的核心由業務索引和底層存儲兩部分組成,由於底層 IO 性能的提升,這兩部分都需要進行深度改造以適應這種結構性的改變。下面我們將分別介紹這兩部分的改造細節。

業務索引

索引服務是分佈式文件系統的核心功能之一。相比對象存儲等其它存儲服務,文件存儲的索引需要提供更爲複雜的語義,所以會對性能產生更大影響。

索引服務的功能模塊設計是基於單機文件系統設計思路的一種『仿生』,分爲兩大部分:

• 目錄索引:實現樹狀層級目錄,記錄各個目錄下的文件和子目錄項

• 文件索引:記錄文件元數據,包含數據塊存儲信息和訪問權限等

索引服務各模塊的功能是明確的,主要解決兩個問題:

• 業務特性:除了實現符合文件系統語義的各類操作外,還要保證索引數據的外部一致性,在各類併發場景下不對索引數據產生靜態修改從而產生數據丟失或損壞

• 分佈式系統特性:包括系統拓展性、可靠性等問題,使系統能夠應對各類節點和數據故障,保證系統對外的高可用性和系統彈性等

雖然功能有區別,目錄索引和文件索引在架構上是類似的,所以我們下面只介紹文件索引 (FileIdx) 架構。在以上的目標指導下,最終 FileIdx 採用無狀態設計,依靠各索引節點和 master 之間的租約(Lease)機制來做節點管理,實現其容災和彈性架構。

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

租約機制和悲觀鎖

master 模塊負責維護一張路由表,路由表可以理解成一個由虛節點組成的一致性哈希環,每個 FileIdx 實例負責其中的部分虛節點,master 通過心跳和各個實例節點進行存活性探測,並用租約機制告知 FileIdx 實例和各個 NFSServer 具體的虛節點由誰負責處理。如果某個 FileIdx 實例發生故障,master 只需要在當前租約失效後將該節點負責的虛節點分配給其他實例處理即可。

當 NFSServer 需要向文件服務請求具體操作 (比如請求分配 IO 塊) 時,會對請求涉及的文件句柄做哈希操作確認負責該文件的虛節點由哪個 FileIdx 處理,將請求發至該節點。每個節點上爲每個文件句柄維持一個處理隊列,隊列按照 FIFO 方式進行執行。本質上這構成了一個悲觀鎖,當一個文件的操作遇到較多併發時,我們保證在特定節點和特定隊列上的排隊,使得併發修改導致的衝突降到最低。

更新保護

儘管租約機制一定程度上保證了文件索引操作的併發安全性,但是在極端情況下租約也不能保持併發操作的絕對互斥及有序。所以我們在索引數據庫上基於 CAS 和 MVCC 技術對索引進行更新保護,確保索引數據不會因爲併發更新而喪失外部一致性。

IO 塊分配優化

在性能型 UFS 中,底層存儲的 IO 延遲大幅降低帶來了更高的 IOPS 和吞吐,也對索引模塊特別是 IO 塊的分配性能提出了挑戰。頻繁地申請 IO 塊導致索引在整個 IO 鏈路上貢獻的延遲比例更高,對性能帶來了損害。一方面我們對索引進行了讀寫分離改造,引入緩存和批量更新機制,提升單次 IO 塊分配的性能。

同時,我們增大了 IO 塊的大小,更大的 IO 數據塊降低了分配和獲取數據塊的頻率,將分配開銷進行均攤。後續我們還將對索引關鍵操作進行異步化改造,讓 IO 塊的分配從 IO 關鍵路徑上移除,最大程度降低索引操作對 IO 性能的影響。

底層存儲

  • 設計理念

存儲功能是一個存儲系統的重中之重,它的設計實現關係到系統最終的性能、穩定性等。通過對 UFS 在數據存儲、數據操作等方面的需求分析,我們認爲底層存儲 (命名爲 nebula) 應該滿足如下的要求:・簡單:簡單可理解的系統有利於後期維護・可靠:必須保證高可用性、高可靠性等分佈式要求・拓展方便:包括處理集羣擴容、數據均衡等操作・支持隨機 IO・充分利用高性能存儲介質

Nebula: append-only 和中心化索引

基於以上目標,我們將底層存儲系統 nebula 設計爲基於 append-only 的存儲系統 (immutable storage)。面向追加寫的方式使得存儲邏輯會更簡單,在多副本數據的同步上可以有效降低數據一致性的容錯複雜度。更關鍵的是,由於追加寫本質上是一個 log-based 的記錄方式,整個 IO 的歷史記錄都被保存,在此之上實現數據快照和數據回滾會很方便,在出現數據故障時,更容易做數據恢復操作。

在現有的存儲系統設計中,按照數據尋址的方式可以分爲去中心化和中心化索引兩種,這兩者的典型代表系統是 Ceph 和 Google File System。去中心化的設計消除了系統在索引側的故障風險點,並且降低了數據尋址的開銷。但是增加了數據遷移、數據分佈管理等功能的複雜度。出於系統簡單可靠的設計目標,我們最終選擇了中心化索引的設計方式,中心化索引使集羣擴容等拓展性操作變得更容易。

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

數據塊管理:extent-based 理念

中心化索引面臨的性能瓶頸主要在數據塊的分配上,我們可以類比一下單機文件系統在這方面的設計思路。早期文件系統的 inode 對數據塊的管理是 block-based,每次 IO 都會申請 block 進行寫入,典型的 block 大小爲 4KB,這就導致兩個問題:1、4KB 的數據塊比較小,對於大片的寫入需要頻繁進行數據塊申請操作,不利於發揮順序 IO 的優勢。2、inode 在基於 block 的方式下表示大文件時需要更大的元數據空間,能表示的文件大小也受到限制。

在 Ext4/XFS 等更先進的文件系統設計中,inode 被設計成使用 extent-based 的方式來實現,每個 extent 不再被固定的 block 大小限制,相反它可以用來表示一段不定長的磁盤空間,如下圖所示:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

顯然地,在這種方式下,IO 能夠得到更大更連續的磁盤空間,有助於發揮磁盤的順序寫能力,並且有效降低了分配 block 的開銷,IO 的性能也得到了提升,更關鍵的是,它可以和追加寫存儲系統非常好地結合起來。我們看到,不僅僅在單機文件系統中,在 Google File System、Windows Azure Storage 等分佈式系統中也可以看到 extent-based 的設計思想。我們的 nebula 也基於這一理念進行了模型設計。

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

  • 存儲架構

Stream 數據流

在 nebula 系統中存儲的數據按照 stream 爲單位進行組織,每個 stream 稱爲一個數據流,它由一個或多個 extent 組成,每次針對該 stream 的寫入操作以 block 爲單位在最後一個 extent 上進行追加寫,並且只有最後一個 extent 允許寫入,每個 block 的長度不定,可由上層業務結合場景決定。而每個 extent 在邏輯上構成一個副本組,副本組在物理上按照冗餘策略在各存儲節點維持多副本,stream 的 IO 模型如下:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

streamsvr 和 extentsvr

基於這個模型,存儲系統被分爲兩大主要模塊:・streamsvr:負責維護各個 stream 和 extent 之間的映射關係以及 extent 的副本位置等元數據,並且對數據調度、均衡等做管控・extentsvr:每塊磁盤對應一個 extentsvr 服務進程,負責存儲實際的 extent 數據存儲,處理前端過來的 IO 請求,執行 extent 數據的多副本操作和修復等

在存儲集羣中,所有磁盤通過 extentsvr 表現爲一個大的存儲池,當一個 extent 被請求創建時,streamsvr 根據它對集羣管理的全局視角,從負載和數據均衡等多個角度選取其多副本所在的 extentsvr,之後 IO 請求由客戶端直接和 extentsvr 節點進行交互完成。在某個存儲節點發生故障時,客戶端只需要 seal 掉當前在寫入的 extent,創建一個新的 extent 進行寫入即可,節點容災在一次 streamsvr 的 rpc 調用的延遲級別即可完成,這也是基於追加寫方式實現帶來的系統簡潔性的體現。

由此,存儲層各模塊的架構圖如下:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

至此,數據已經可以通過各模塊的協作寫入到 extentsvr 節點,至於數據在具體磁盤上的存儲佈局,這是單盤存儲引擎的工作。

  • 單盤存儲引擎

前面的存儲架構講述了整個 IO 在存儲層的功能分工,爲了保證性能型 UFS 的高性能,我們在單盤存儲引擎上做了一些優化。

線程模型優化

存儲介質性能的大幅提升對存儲引擎的設計帶來了全新的需求。在容量型 UFS 的 SATA 介質上,磁盤的吞吐較低延遲較高,一臺存儲機器的整體吞吐受限於磁盤的吞吐,一個單線程 / 單進程的服務就可以讓磁盤吞吐打滿。隨着存儲介質處理能力的提升,IO 的系統瓶頸逐漸從磁盤往處理器和網絡帶寬方面轉移。

在 NVMe SSD 介質上由於其多隊列的並行設計,單線程模型已經無法發揮磁盤性能優勢,系統中斷、網卡中斷將成爲 CPU 新的瓶頸點,我們需要將服務模型轉換到多線程方式,以此充分發揮底層介質多隊列的並行處理能力。爲此我們重寫了編程框架,新框架採用 one loop per thread 的線程模型,並通過 Lock-free 等設計來最大化挖掘磁盤性能。

block 尋址

讓我們思考一個問題,當客戶端寫入了一片數據 block 之後,讀取時如何找到 block 數據位置?一種方式是這樣的,給每個 block 分配一個唯一的 blockid,通過兩級索引轉換進行尋址:

・第一級:查詢 streamsvr 定位到 blockid 和 extent 的關係

・第二級:找到 extent 所在的副本,查詢 blockid 在 extent 內的偏移,然後讀取數據

這種實現方式面臨兩個問題,(1)第一級的轉換需求導致 streamsvr 需要記錄的索引量很大,而且查詢交互會導致 IO 延遲升高降低性能。(2)第二級轉換以 Facebook Haystack 系統爲典型代表,每個 extent 在文件系統上用一個獨立文件表示,extentsvr 記錄每個 block 在 extent 文件中的偏移,並在啓動時將全部索引信息加載在內存裏,以提升查詢開銷,查詢這個索引在多線程框架下必然因爲互斥機制導致查詢延遲,因此在高性能場景下也是不可取的。而且基於文件系統的操作讓整個存儲棧的 IO 路徑過長,性能調優不可控,也不利於 SPDK 技術的引入。

爲避免上述不利因素,我們的存儲引擎是基於裸盤設計的,一塊物理磁盤將被分爲幾個核心部分:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

superblock: 超級塊,記錄了 segment 大小,segment 起始位置以及其他索引塊位置等

segment: 數據分配單位,整個磁盤除了超級塊以外,其他區域全部都是 segment 區域,每個 segment 是定長的 (默認爲 128MB),每個 extent 都由一個或多個 segment 組成

extent index / segment meta region: extent/segment 索引區域,記錄了每個 extent 對應的 segment 列表,以及 segment 的狀態 (是否可用) 等信息

基於這個設計,我們可以將 block 的尋址優化爲無須查詢的純計算方式。當寫完一個 block 之後,將返回該 block 在整個 stream 中的偏移。客戶端請求該 block 時只需要將此偏移傳遞給 extentsvr,由於 segment 是定長的,extentsvr 很容易就計算出該偏移在磁盤上的位置,從而定位到數據進行讀取,這樣就消除了數據尋址時的查詢開銷。

  • 隨機 IO 支持:FileLayer 中間層

我們之前出於簡單可靠的理念將存儲系統設計爲 append-only,但是又由於文件存儲的業務特性,需要支持覆蓋寫這類隨機 IO 場景。

因此我們引入了一箇中間層 FileLayer 來支持隨機 IO,在一個追加寫的引擎上實現隨機寫,該思路借鑑於 Log-Structured File System 的實現。LevelDB 使用的 LSM-Tree 和 SSD 控制器裏的 FTL 都有類似的實現,被覆蓋的數據只在索引層面進行間接修改,而不是直接對數據做覆蓋寫或者是 COW (copy-on-write),這樣既可以用較小的代價實現覆蓋寫,又可以保留底層追加寫的簡單性。

FileLayer 中發生 IO 操作的單元稱爲 dataunit,每次讀寫操作涉及的 block 都在某個 dataunit 上進行處理,dataunit 的邏輯組成由如下幾個部分:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

dataunit 由多個 segment 組成 (注意這和底層存儲的 segment 不是一個概念),因爲基於 LSM-Tree 的設計最終需要做 compaction, 多 segment 的劃分類似於 LevelDB 中的多層 sst 概念,最下層的 segment 是隻讀的,只有最上層的 segment 允許寫入,這使得 compaction 操作可以更簡單可靠地進行甚至回滾,而由於每次 compaction 涉及的數據域是確定的,也便於我們檢驗 compaction 操作的 invariant:回收前後數據域內的有效數據必須是一樣的。

每個 segment 則由一個索引流和一個數據流組成,它們都存儲在底層存儲系統 nebula 上,每次寫入 IO 需要做一次數據流的同步寫,而爲了提升 IO 性能,索引流的寫入是異步的,並且維護一份純內存索引提升查詢操作性能。爲了做到這一點,每次寫入到數據流中的數據是自包含的,這意味着如果索引流缺失部分數據甚至損壞,我們可以從數據流中完整構建整個索引。

客戶端以文件爲粒度寫入到 dataunit 中,dataunit 會給每個文件分配一個全局唯一的 fid,fid 作爲數據句柄存儲到業務索引中 (FileIdx 的 block 句柄)。

dataunit 本身則由 fileserver 服務進程負責,每個 fileserver 可以有多個 dataunit,coordinator 根據各節點的負載在實例間進行 dataunit 的調度和容災。整個 FileLayer 的架構如下:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

至此,存儲系統已經按照設計要求滿足了我們文件存儲的需求,下面我們來看一看各個模塊是如何一起協作來完成一次文件 IO 的。

The Big Picture:一次文件寫 IO 的全流程

從整體來說,一次文件寫 IO 的大致流程是這樣的:

①用戶在主機上發起 IO 操作會在內核層被 nfs-client 在 VFS 層截獲 (僅以 Linux 系統下爲例),通過被隔離的 VPC 網絡發往 UFS 服務的接入層。

②接入層通過對 NFS 協議的解析和轉義,將這個操作分解爲索引和數據操作。

③經過索引模塊將這個操作在文件內涉及的 IO 範圍轉化爲由多個 file system block (固定大小,默認 4MB) 表示的 IO 範圍。

④NFSServer 拿到需要操作的 block 的句柄 (bid) 後去請求 FileLayer 進行 IO 操作 (每個 bid 在 FileLayer 中代表一個文件)。

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

請求會被 NFSServer 發往負責處理該 bid 對應的文件的 fileserver 上,fileserver 獲取該文件所在的 dataunit 編號 (此編號被編碼在 bid 中) 後,直接往該 dataunit 當前的數據流 (stream) 中進行追加寫,完成後更新索引,將新寫入的數據的位置記錄下來,本次 IO 即告完成,可以向 NFSServer 返回迴應了。類似地,當 fileserver 產生的追加寫 IO 抵達其所屬的 extentsvr 的時候,extentsvr 確定出該 stream 對應的最後一個 extent 在磁盤上的位置,並執行一次追加寫落地數據,在完成多副本同步後返回。

至此,一次文件寫 IO 就完成了。

性能數據

經過前述的設計和優化,性能型 UFS 的實際性能數據如下:

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

總結

本文從 UFS 性能型產品的需求出發,詳細介紹了基於高性能存儲介質構建分佈式文件系統時,在協議、業務架構、存儲引擎等多方面的設計考慮和優化,並最終將這些優化落實到產品中去。性能型 UFS 的上線豐富了產品種類,各類對 IO 延遲要求更高的大數據分析、AI 訓練等業務場景將得到更好的助力。

後續我們將在多方面繼續提升 UFS 的使用體驗,產品上會支持 SMB 協議,提升 Windows 主機使用文件存儲的性能;底層存儲會引入 SPDK、RDMA 等技術,並結合其它更高性能的存儲介質;在冷存數據場景下引入 Erasure Coding 等技術;使用戶能夠享受到更先進的技術帶來的性能和價格紅利。

基於NVMe SSD的分佈式文件存儲UFS性能提升技術解析

產品最新優惠:性能型 UFS 原價 1.0 元 /GB/ 月,現在福建可用區優惠價 0.6 元 /GB/ 月,國內其他可用區 0.8 元 /GB/ 月,歡迎聯繫客戶經理申請體驗!

如您有本篇文章相關問題,歡迎添加作者微信諮詢。WeChat ID:cheneydeng

 

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