BlueStore-先進的用戶態文件系統《一》


https://zhuanlan.zhihu.com/p/45084771

分布式存儲系統通過將數據分散到多台機器上來充分利用多台機器的資源提高系統的存儲能力,每台機器上的數據存放都需要本地的單機存儲系統,它是整個分布式存儲系統的基礎,為其提供保障。設計高性能、高可靠的分布式存儲系統離不開高效、一致、穩定、可靠的本地存儲系統。

ceph是目前業內比較普遍使用的開源分布式存儲系統,實現有多種類型的本地存儲系統;在較早的版本當中,ceph默認使用FileStore作為后端存儲,但是由於FileStore存在一些缺陷,重新設計開發了BlueStore,並在L版本之后作為默認的后端存儲。BlueStore的一些設計思想對於設計滿足分布式存儲系統需求的本地存儲系統具有參考意義,因此我們將分多個章節對BlueStore的一些原理進行剖析,供讀者進行參考和探討。

在這一章中,我們將要了解BlueStore的誕生背景,以及它的一些設計思想。

為什么需要BlueStore

前面提到,BlueStore的誕生背景是由於FileStore存在的一些缺陷,這些缺陷具體是什么?

 

  • IO放大

FileStore底層使用POSIX規范的文件系統接口,例如xfs、ext4、btrfs,然而這類文件系統本身不支持數據或元數據的事務操作接口(btrfs提供事務鈎子的接口,但是測試過程中發現會導致系統宕機),而ceph對於數據寫入要求十分嚴格,需要滿足事務的特性(ACID);為此FileStore實現了FileJournal功能,所有的事務都需要先寫到FileJournal中,之后才會寫入對應的文件中,以此來保證事務的原子性,但是這導致了數據“雙寫”的問題,造成至少一半磁盤帶寬的浪費。

此外xfs、ext4、btrfs這類文件系統本身存在一定的IO放大(即一次讀寫請求實際在低層磁盤發生的IO次數),再加上FileStore的日志雙寫,放大倍數成倍增加。

下圖中的數據表示了以block大小為單位對不同文件系統進行讀寫,在不同場景下的讀寫放大及空間放大情況。我們以ext4文件系統說明下個參數的含義。在對文件進行Overwrite時,即將數據覆蓋寫入到文件中,除了寫入數據外,還涉及到日志的寫入(其中日志寫入兩次,一次記錄更改的inode,一次為commit記錄,具體可參考[5])、文件inode的更改,每次寫的最小單位是block,因此最終相當於寫入次數以及空間放大了四倍;而在進行Append寫入時,由於需要新分配空間,因此相對於Overwrite增加了bitmap的更改以及superblock的更改(superblock記錄總的空間分配情況),寫放大和空間放大均為六倍。讀文件時,在沒有命中任何緩存的情況下(cold cache),需要讀大量元數據,例如:目錄、文件inode、superblock等,最終讀放大為六倍;而如果是在順序讀的情況下(warm cache),像superblock、bitmap、目錄等這些元數據都緩存在內存中,只需讀取文件inode和文件數據。

同理,其他文件系統由於不同的結構和設計原理,其IO放大和空間放大系數也各不相同。

 

  • 對象遍歷

ceph的數據被划分為object存放,object以32位的hash值進行標識,ceph在進行scrubbing、backfill或者recovery時都需要根據hash值遍歷這些object;POSIX文件系統不提供有序的文件遍歷接口,為此FileStore根據文件的數量和hash的前綴將object划分到不同的子目錄,其原則如下:

  • 當目錄下的文件個數>100個時,拆分子目錄;目錄名以文件名的hash前綴為依據(拆分一級目錄時,以hash第一位為拆分依據,二級目錄以第二位hash為拆分依據,依次類推)
  • 當所有子目錄下的文件個數<50個時,將合並到上級目錄

因此FileStore在使用過程中需要不斷合並拆分目錄結構;這種方式將文件按照前綴放到不同目錄,但對於同一目錄中的文件依然無法很好排序,因此需要將目錄中的所有文件讀到內存進行排序,這樣在一定程度上增加了CPU開銷。

 

  • 其他
    • FileStore由於設計的較早,無法支持當前較新的存儲技術,例如使用spdk技術讀寫NVMe盤。
    • 數據和元數據分離不徹底。
    • 流控機制不完整導致IOPS和帶寬抖動(FileStore自身無法控制本地文件系統的刷盤行為)。
    • 頻繁syncfs系統調用導致CPU利用率居高不下。

BlueStore介紹

需求

首先看下BlueStore設計之初的一些需求:

  • 對全SSD及全NVMe SSD閃存適配
  • 繞過本地文件系統層,直接管理裸設備,縮短IO路徑
  • 嚴格分離元數據和數據,提高索引效率
  • 使用KV索引,解決文件系統目錄結構遍歷效率低的問題
  • 支持多種設備類型
  • 解決日志“雙寫”問題
  • 期望帶來至少2倍的寫性能提升和同等讀性能
  • 增加數據校驗及數據壓縮等功能

邏輯架構

 

BlueStore的邏輯架構如上圖所示,模塊的划分都還比較清晰,我們來看下各模塊的作用:

  • RocksDB:rocksdb是facebook基於leveldb開發的一款kv數據庫,BlueStore將元數據全部存放至RocksDB中,這些元數據包括存儲預寫式日志、數據對象元數據、Ceph的omap數據信息、以及分配器的元數據 。
  • BlueRocksEnv:這是RocksDB與BlueFS交互的接口;RocksDB提供了文件操作的接口EnvWrapper,用戶可以通過繼承實現該接口來自定義底層的讀寫操作,BlueRocksEnv就是繼承自EnvWrapper實現對BlueFS的讀寫。
  • BlueFS:BlueFS是BlueStore針對RocksDB開發的輕量級文件系統,用於存放RocksDB產生的.sst和.log等文件。
  • BlockDecive:BlueStore拋棄了傳統的ext4、xfs文件系統,使用直接管理裸盤的方式;BlueStore支持同時使用多種不同類型的設備,在邏輯上BlueStore將存儲空間划分為三層:慢速(Slow)空間、高速(DB)空間、超高速(WAL)空間,不同的空間可以指定使用不同的設備類型,當然也可使用同一塊設備,具體我們會在后面的文章進行說明。
  • Allocator:負責裸設備的空間管理,只在內存做標記,目前支持StupidAllocator和BitmapAllocator兩種分配器,Stupid基於extent的方式實現 。

設計思想

在設計分布式文件系統的本地存儲時,我們必須考慮數據的一致性和可靠性。在數據寫入的過程中,由於可能存在異常掉電、進程崩潰等突發情況,導致數據還未全部寫入成功便結束。雖然硬盤本身可以保證在扇區級別寫入的原子性,但是一般文件系統的一個寫請求通常包含多個扇區的數據和元數據更新,無法做到原子寫。

常用的解決辦法是引入日志系統,數據寫入磁盤之前先寫到日志系統,然后再將數據落盤;日志寫入成功后,即便寫數據時出現異常,也可以通過日志回放重新寫入這部分數據;如果寫日志的過程中出現異常,則直接放棄這部分日志,視為寫入失敗即可,以此保證原子寫入。但是這種方式導致每份數據都需要在磁盤上寫入兩次,嚴重降低了數據的寫入效率。

另一種方式則是采用ROW(Redirect on write)的方式,即數據需要覆蓋寫入時,將數據寫到新的位置,然后更新元數據索引,這種方式由於不存在覆蓋寫,只需保證元數據更新的原子性即可。對於對齊的覆蓋寫入時,這種方式沒有問題,但是如果是非對齊的覆蓋寫呢?

我們舉個例子:某文件的邏輯空間 [0,4096) 區間的數據在磁盤上的物理映射地址為[0, 4096),磁盤的塊(即磁盤讀寫的最小單元)大小為4096;如果要覆蓋寫文件[0,4096)區間的數據,那使用ROW的方式沒有問題,重新再磁盤上分配一個新的塊寫入,然后更新元數據中的映射關系即可;但是如果寫文件[512,4096)區域,也就是非對齊的覆蓋寫時,新分配的塊中只有部分數據,舊的物理空間中仍有部分數據有效,這樣元數據中需要維護兩份索引,而且在讀取文件的該塊數據時,需要從多塊磁盤塊中讀取數據,如果多次進行非對齊覆蓋寫,這種問題將更嚴重。

解決這種問題辦法是使用RMW(Read Modify Write)的方法,即在發生非對齊覆蓋寫時,先讀取舊的數據,更新的數據合並后,對齊寫入到磁盤中,從而減少元數據、提高讀性能,但這種方式也存在一種缺點,寫數據時需要先讀數據,存在一定的性能損耗。

分析完ROW的方式后,讀者是否會有疑問,每次寫入都放到新的位置,那么文件在磁盤中的物理連續性豈不是無法保證?的確,在傳統的文件系統設計時,都是面向HDD盤,這種類型的盤在讀寫時會有磁頭尋道的時間,對於非連續的物理空間讀寫,性能極差,在設計時會盡可能考慮數據存放的連續性,因此很少會采用ROW的方式。但是隨着SSD盤的逐漸普及,隨機讀寫的性能不再成為主要的性能關注點,越來越多的存儲系統開始采用全閃存的磁盤陣列,相信ROW的方式會成為更加主流的方式。

我們再來看下BlueStore是怎么實現的,BlueStore在設計時便考慮了全閃存的磁盤陣列,但是仍要考慮使用HDD盤的場景,因此並未完全采用ROW的方式。

我們以下圖為例進行說明,BlueStore提供了一個最小分配單元min_alloc_size的配置項,一般為磁盤塊大小的整數倍,在此例中min_alloc_size為block大小的4倍。

寫入的數據如果與min_alloc_size大小對齊,則使用ROW的方式,將數據寫到新的地址空間,然后更改元數據索引,並回收原先占用的空間,元數據更新的原子性由RocksDB的事務特性進行保障。

而對於非min_alloc_size對齊的區域,則使用RMW的方式進行原地覆蓋寫(只讀取非塊大小對齊區域所在塊,一般就是寫入數據的第一個或最后一個塊),寫入的這部分數據可能跨多個塊(因為min_alloc_size是塊大小的整數倍),而磁盤只保證單個塊大小的原子寫入,對於多個塊的原子寫需要引入類似日志的功能,BlueStore用RocksDB來實現日志功能,將覆蓋的這部分數據記到RocksDB中,完成以后再將數據覆蓋寫入到實際的數據區域,落盤成功以后再刪除日志中的記錄。

 

BlueStore對於寫的流程處理較為復雜,我們將在后面的章節中詳細進行分析。

總結

BlueStore的設計考慮了FileStore中存在的一些硬傷,拋棄了傳統的文件系統直接管理裸設備,縮短了IO路徑,同時采用ROW的方式,避免了日志雙寫的問題,在寫入性能上有了極大的提高。

通過分析BlueStore的基本結構、考慮的問題以及設計思想,我們對於BlueStore有了大概的了解;BlueStore在設計時有考慮到未來存儲的應用環境,是一種比較先進的本地文件系統,但也不可避免存在一些缺陷,例如較為復雜的元數據結構和IO邏輯,在大量小IO下可能存在的double write問題,較大的元數據內存占用等(當然有些問題在ceph的使用場景下可能不存在,但是我們如果希望借鑒BlueStore來設計本地文件系統就不得不考慮這些問題)。

在后續的系列文章中,我們將繼續深入剖析各個模塊的設計原理和流程,也會對BlueStore測試過程中發現的一些問題展開討論。

 

Notes

作者:網易存儲團隊工程師 楊耀凱。限於作者水平,難免有理解和描述上有疏漏或者錯誤的地方,歡迎共同交流;部分參考已經在正文和參考文獻中列表注明,但仍有可能有疏漏的地方,有任何侵權或者不明確的地方,歡迎指出,必定及時更正或者刪除;文章供於學習交流,轉載注明出處

參考資料

[1]. New in Luminous: BlueStore. https://ceph.com/community/new-luminous-bluestore/.

[2]. ceph存儲引擎bluestore解析. https://www.sysnote.org/2016/08/19/ceph-bluestore/.

[3]. Jayashree Mohan, Rohan Kadekodi, Vijay Chidambaram. Analyzing IO Amplification in Linux File Systems. arXiv:1707.08514v1 [cs.OS] 26 Jul 2017 .

[4]. 謝型果等. Ceph設計原理與實現[M]. 北京:機械工業出版社,2017.12.

[5]. Linux: The Journaling Block Device. 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM