為什么需要 BlueStore
首先,Ceph原本的FileStore需要兼容Linux下的各種文件系統,如EXT4、BtrFS、XFS。理論上每種文件系統都實現了POSIX協議,但事實上,每個文件系統都有一點“不那么標准”的地方。Ceph的實現非常注重可靠性,因而需要為每種文件系統引入不同的Walkaround或者Hack;例如Rename不冪等性,等等。這些工作為Ceph的不斷開發帶來了很大負擔。
其次,FileStore構建與Linux文件系統之上。POSIX提供了非常強大的功能,但大部分並不是Ceph真正需要的;這些功能成了性能的累贅。另一方面,文件系統的某些功能實現對Ceph並不友好,例如對目錄遍歷順序的要求,等等。
另一方面,是Ceph日志的雙寫問題。為了保證覆寫中途斷電能夠恢復,以及為了實現單OSD內的事物支持,在FileStore的寫路徑中,Ceph首先把數據和元數據修改寫入日志,日志完后后,再把數據寫入實際落盤位置。這種日志方法(WAL)是數據庫和文件系統標准的保證ACID的方法。但用在Ceph這里,帶來了問題:
數據被寫入兩遍,即日志雙寫問題,這意味着Ceph犧牲了一半的磁盤吞吐量。
- Ceph的FileStore做了一遍日志,而Linux文件系統自身也有日志機制,實際上日志被多做了一遍。
- 對於新型的LSM-Tree類存儲,如RocksDB、LevelDB,由於數據本身就按照日志形式組織,實際上沒有再另加一個單獨的WAL的必要。
- 更好地發揮SSD/NVM存儲介質的性能。與磁盤不同,基於Flash的存儲有更高的並行能力,需要加以利用。CPU處理速度逐漸更不上存儲,因而需要更好地利用多核並行。存儲中大量使用的隊列等,容易引發並發競爭耗時,也需要優化。另一方面,RocksDB對SSD等有良好支持,它為BlueStore所采用。
另外,社區曾經為了FileStore的問題,提出用LevelDB作存儲后端;對象存儲轉換為KeyValue存儲,而不是轉換問文件。后來,LevelDB存儲沒有被推廣開,主流還是使用FileStore。但KeyValue的思路被沿用下來,BlueStore就是使用RocksDB來存儲元數據的。
BlueStore 整體架構
bluestore 的誕生是為了解決 filestore 自身維護一套journal 並同時還需要基於文件系統的寫放大問題,並且 filestore 本身沒有對 SSD 進行優化,因此 bluestore 相比於 filestore 主要做了兩方面的核心工作:
- 去掉 journal ,直接管理裸設備
- 針對 SSD 進行單獨優化
bluestore 整體架構如下圖:

通過Allocator(分配器)實現對裸設備的管理,直接將數據保存到設備上;同時針對 metadata 使用 RocksDB 進行保存,底層自行封裝了一個BlueFS用來對接RocksDB 與裸設備。
模塊划分
核心模塊
- RocksDB: 存儲預寫式日志、數據對象元數據、Ceph的omap數據信息、以及分配器的元數據(分配器負責決定真正的數據應在什么地方存儲)
- BlueRocksEnv: 與RocksDB交互的接口
- BlueFS: 小的文件系統,解決元數據、文件空間及磁盤空間的分配和管理,並實現了rocksdb::Env 接口(存儲RocksDB日志和sst文件)。因為rocksdb常規來說是運行在文件系統的頂層,下面是BlueFS。 它是數據存儲后端層,RocksDB的數據和BlueStore中的真正數據被存儲在同一個塊物理設備
- BlockDevice(HDD/SSD): 物理塊設備,存儲實際的數據
rocksdb本身是基於文件系統的,不是直接操作裸設備。它將系統相關的處理抽象成Env,用戶可用實現相應的接口。BlueRocksEnv是bluestore實現的一個類,繼承自rocksdb::EnvWrapper,來為rocksdb提供底層系統的封裝。
為了對接BlueRocksEnv,實現了一個小的文件系統BlueFS,只實現rocksdb Env需要的接口。所有的元數據的修改都記錄在BlueFS的日志中,也就是對於BlueFS,元數據的持久化保存在日志中。在系統啟動mount這個文件系統時,只需回放日志,就可將所有的元數據都加載到內存中。BluesFS的數據和日志文件都通過塊設備保存到裸設備上(BlueFS和BlueStore可以共享裸設備,也可以分別指定不同的設備)。
bluestore不使用本地文件系統,直接接管裸設備,並且只使用一個原始分區,HDD/SSD所在的物理塊設備實現在用戶態下使用linux aio直接對裸設備進行I/O操作。由於操作系統支持的aio操作只支持directIO,所以對BlockDevice的寫操作直接寫入磁盤,並且需要按照page對齊。其內部有一個aio_thread 線程,用來檢查aio是否完成。其完成后,通過回調函數aio_callback 通知調用方。
Allocator 模塊
用來委派具體哪個實際存儲塊用來存儲當前的object數據;同樣采用bitmap的方式來實現allocator,同時采用層級索引來存儲多種狀態,這種方式對內存的消耗相對較小,平均1TB磁盤需要大概35M左右的ram空間。
BlueStore 模塊
在之前的存儲引擎filestore里,對象的表現形式是對應到文件系統里的文件,默認4MB大小的文件,但是在bluestore里,已經沒有傳統的文件系統,而是自己管理裸盤,因此需要有元數據來管理對象,對應的就是Onode,Onode是常駐內存的數據結構,持久化的時候會以kv的形式存到rocksdb里。
BlueStore 存儲的最常用寫路徑應該盡量的短,盡量的簡單,這樣才能有最好的性能,盡快另外的異常處理路徑可能是非常復雜的。BlueStore 的設計有如下特色:
- Ceph 並不需要POSIX 文件系統。拋棄它,實現一個盡量簡單的文件系統,專門給 RocksDB 使用。這個文件系統叫做 BlueFS
- 元數據存儲在RocksDB中,用KeyValue的方式正合適。而數據不需要文件系統,直接存儲在裸塊設備上即可。我們在塊設備上需要的,其實是一個空間分配器(Allocator)。
還有一點,BlueStore 中不同組件可以使用不同的設備。例如給 RocksDB 的 WAL 文件配置 NVRAM,給SST文件配備 SSD,給數據文件配備HDD,方案是靈活的。
BlueStore 的元數據管理
在涉及寫路徑之前,先看看Ceph BlueStore 如何管理元數據。首先的問題是,對象如何映射成磁盤數據結構(Ceph 的底層是對象存儲,向上封裝出塊存儲、文件存儲)?
Onode代表對象,名字大概是從Linux VFS的Inode沿襲過來的。Onode常駐內存,在RocksDB中以KeyValue形式持久化;
Onode包含多個lextent,即邏輯extent。Blob通過映射pextent、即物理extent,映射到磁盤上的物理區域。Blob通常包括來自同一個對象的多段數據,但是也可能被其它對象引用。Bnode是對象快照后,被用於多個對象共享數據的。(不太懂)
BlueStore 的寫路徑
寫路徑包含了對事務的處理,也回答了BlueStore如何解決日志雙寫問題。
首先,Ceph的事務只工作於單個OSD內,能夠保證多個對象操作被ACID地執行,主要是用於實現自身的高級功能。每個PG(Placement Group,類似Dynamo的vnode,將hash映射到同一個組內的對象組到一起)內有一個OpSequencer,通過它保證PG內的操作按序執行。事務需要處理的寫分三種:
(1)寫到新分配的區域。考慮ACID,因為此寫不覆蓋已有數據,即使中途斷電,因為RocksDB中的元數據沒有更新,不用擔心ACID語義被破壞。后文可見RocksDB的元數據更新是在數據寫之后做的。因而,日志是不需要的。在數據寫完之后,元數據更新寫入RocksDB;RocksDB本身支持事務,元數據更新作為RocksDB的事務提交即可。
(2)寫到Blob中的新位置。同理,日志是不需要的。
(3)Deferred Writes(延遲寫),只用於覆寫(Overwrite)情況。從上面也可以看到,只有覆寫需要考慮日志問題。如果新寫比塊大小(min_alloc_size)更小,那么會將其數據與元數據合並寫入到RocksDB中,之后異步地把數據搬到實際落盤位置;這就是日志了。如果新寫比塊大小更大,那么分割它,整塊的部分寫入新分配塊中,即按(1)處理,;不足的部分按(3)中上種情況處理。
上述基本概述了BlueStore的寫處理。可以看到其是如何解決FileStore的日志雙寫問題的。
首先,沒有Linux文件系統了,也就沒有了多余的Journaling of Journal問題。然后,大部分寫是寫到新位置的,而不是覆寫,因此不需要對它們使用日志;寫仍然發生了兩次,第一次是數據落盤,然后是RocksDB事務提交,但不再需要在日志中包含數據了。最后,小的覆寫合並到日志中提交,一次寫完即可返回用戶,之后異步地把數據搬到實際位置(小數據合並到日志是個常用技巧);大的覆寫被分割,整塊部分用Append-only方式處理,也繞開了日志的需要。至此,成為一個自然而正常的處理方式。
BlueFS 的架構
BlueFS以盡量簡單為目的設計,專門用於支持RocksDB;RocksDB總之還是需要一個文件系統來工作的。BlueFS不支持POSIX接口。總的來說,它有這些特點:
(1)目錄結構方面,BlueFS只有扁平的目錄結構,沒有樹形層次關系;用於放置RocksDB的db.wal/,db/,db.slow/文件。這些文件可以被掛載到不同的硬盤上,例如db.wal/放在NVMRAM上;db/包含熱SST數據,放在SSD上;db.slow/放在磁盤上。
(2)數據寫入方面,BlueFS不支持覆寫,只支持追加(Append-only)。塊分配粒度較粗,約1MB。有垃圾回收機制定期處理被浪費掉的空間。
(3)對元數據的操作記錄到日志,每次掛載時重放日志,來獲得當前的元數據。元數據生存在內存中,並沒有持久化在磁盤上,不需要存儲諸如空閑塊鏈表之類的。當日志過大時,會進行重寫Compact。
如果問為什么BlueStore相比FileStore能夠提高越一倍的吞吐量,可能在於其更加簡單、更加短的寫路徑;解決了雙寫問題,大部分數據不再需要在日志中多寫一遍;借用RocksDB處理元數據,后者實現成熟,對SSD優化良好。
總結
BlueStore 最大的特點是 OSD 可以直接管理裸磁盤設備,並且將對象數據存儲在該設備中。另外對象有很多KV屬性信息,這些信息之前是存儲在文件的擴展屬性或者LevelDB當中的。而在BlueStore中,這些信息存儲在RocksDB當中。RocksDB本身是需要運行在文件系統之上的,因此為了使用RocksDB存儲這些元數據,需要開發一個簡單的文件系統(BlueFS)。
從BlueStore 的設計和實現上看,可以將其理解為用戶態下的一個文件系統,同時使用RocksDB來實現BlueStore所有元數據的管理,簡化實現。
對於整塊數據的寫入,數據直接以aio的方式寫入磁盤,再更新RocksDB中數據對象的元數據,避免了filestore的先寫日志,后apply到實際磁盤的兩次寫盤。同時避免了日志元數據的冗余存儲占用,因為傳統文件系統有他們自己內部的日志和元數據管理機制。
BlueStore 其實是實現了用戶態的一個文件系統。為了實現簡單,又使用了RocksDB來實現了BlueStore的所有元數據的管理,簡化了實現。
優點在於:
對於整塊數據的寫入,數據直接 AIO 的方式寫入磁盤,避免了 filestore的先寫日志,后 apply到實際磁盤的兩次寫盤。
對於隨機IO,直接 WAL 的形式,直接寫入 RocksDB 高性能的 KV 存儲中。
參考鏈接:
https://blog.csdn.net/u010487568/article/details/79572390
https://cloud.tencent.com/developer/news/45599
