ceph存儲引擎bluestore解析


原文鏈接:http://www.sysnote.org/2016/08/19/ceph-bluestore/

ceph后端支持多種存儲引擎,以插件式的方式來進行管理使用,目前支持filestorekvstorememstore以及最新的bluestore,目前默認使用的filestore,但是因為filestore在寫數據前需要先寫journal,會有一倍的寫放大,並且filestore一開始只是對於機械盤進行設計的,沒有專門針對ssd做優化考慮,因此誕生的bluestore初衷就是為了減少寫放大,並針對ssd做優化,而且直接管理裸盤,從理論上進一步減少文件系統如ext4/xfs等部分的開銷,目前bluestore還處於開發優化階段,在jewel版本還是試用版本,並且最新的master相比jewel已經做了大的重構,預期會在后續的大版本中穩定下來成為默認的存儲引擎。本文基於master分支對bluestore存儲引擎進行分析。

bluestore整體架構

bluestore直接管理裸設備,拋棄了ext4/xfs等本地文件系統,BlockDevice實現在用戶態下使用linux aio直接對裸設備進行I/O操作。既然是直接管理裸設備就必然需要進行裸設備的空間管理,對應的就是Allocator,目前支持StupidAllocatorBitmapAllocator兩種分配器。相關的元數據以kv的形式保存到kv數據庫里,默認使用的是rocksdb,由於rocksdb本身是基於文件系統的,不是直接操作裸設備,但是rocksdb也比較靈活,將系統相關的處理抽象成Env,用戶可用實現相應的接口,rocksdb默認的EnvPosixEnv,直接對接本地文件系統,為此,bluestore實現了一個BlueRocksEnv,繼承自EnvWrapper,來為rocksdb提供底層系統的封裝,為了對接BlueRocksEnv,實現了一個小的文件系統BlueFS,只實現rocksdb Env需要的接口,在系統啟動mount這個文件系統的時候將所有的元數據都加載到內存中,BluesFS的數據和日志文件都通過BlockDevice保存到裸設備上,BlueFSBlueStore可以共享裸設備,也可以分別指定不同的設備。

bluestore元數據


在之前的存儲引擎filestore里,對象的表現形式是對應到文件系統里的文件,默認4MB大小的文件,但是在bluestore里,已經沒有傳統的文件系統,而是自己管理裸盤,因此需要有元數據來管理對象,對應的就是OnodeOnode是常駐內存的數據結構,持久化的時候會以kv的形式存到rocksdb里。

onode里又分為lextent,表示邏輯的數據塊,用一個map來記錄,一個onode里會存在多個lextentlextent通過blobid對應到blobbluestore_blob_t ),blob里通過pextent對應到實際物理盤上的區域(pextent里就是offsetlength來定位物理盤的位置區域)。一個onode里的多個lextent可能在同一個blob里,而一個blob也可能對應到多個pextent
另外還有Bnode這個元數據,它是用來表示多個object可能共享extent,目前在做了快照后寫I/O觸發的cow進行clone的時候會用到。

I/O讀寫映射邏輯

I/O處理

到達bluestoreI/Ooffsetlength都是對象內(onode)的,offset是相對於這個對象起始位置的偏移,在_do_write里首先就會根據最小分配單位min_alloc_size進行判斷,從而將I/O分為對齊和非對齊的。如下圖所示:

當一個寫請求按照min_alloc_size進行拆分后,就會分為對齊寫,對應到do_write_big,非對齊寫(即落到某一個min_alloc_size區間的寫I/O(對應到do_write_small)。

do_write_big

對齊到min_alloc_size的寫請求處理起來比較簡單,有可能是多個min_alloc_size的大小,在處理時會根據實際大小新生成lextentblob,這個lextent跨越的區域是min_alloc_size的整數倍,如果這段區間是之前寫過的,會將之前的lextent記錄下來便於后續的空間回收。

do_write_small

在處理落到某個min_alloc_size區間的寫請求時,會首先根據offset去查找有沒有可以復用的blob,因為最小分配單元是min_alloc_size,默認64KB,如果一個4KB的寫I/O就只會用到blob的一部分,blob里剩余的還能放其他的。

1)沒有找到可以復用的blob,新生成blob


在處理還還需要根據offsetlen是否對齊到block_size(默認是4KB)進行補零對齊的操作,之所以需要補齊是與后續的寫盤操作有關,真正寫盤時有兩種方式,一種是Direct I/O的方式,這種要求偏移和緩沖區都對齊的,另外一種非Direct I/O,即Buffered I/O,這種可以不對齊,但是是寫到cache里,然后再sync刷到磁盤上,比如只寫了100字節,在內核里是需要先從設備上讀出來補齊成一個完整的扇區,然后再刷的,這樣反而降低了效率。因此在bluestore里直接處理好對齊,對於后面的寫盤來說比較有利,這里對齊到block_size,是個可配置的參數。

進行對齊補零時就是按照如上圖那樣把前后對齊到block_size,然后再把對齊后的offsetlen作為lextent,進而放到blob里。

2)找到可以復用的blob

對於可以復用的blob,也是先按照block_size進行對齊補零的動作,然后再判斷是否可以直接使用blob里空閑的空間進行區分做不同的處理。

a)直接寫在blob未使用的空間上

這種情況下直接新生成lextent放到blob里。

b
)覆蓋寫的情況

比如下面的這種情況,寫I/O會覆蓋部分已經寫過的數據。

對於這種情況的處理如下圖:也是需要先處理對齊補零的情況,如果覆蓋的區域剛好是已經對齊到block_size,那么就不需要從磁盤讀數據,但是如果覆蓋的區域沒有對齊到block_size,那么就需要把不對齊的那部分讀出來,拼成一個對齊的buffer,然后新生成lextent,並且會對原來那個lextent進行調整,會記錄需要回收的那部分區域。對於覆蓋寫的情況,都不是直接寫盤,而是通過wal寫到rocksdb

整體寫I/O的邏輯

之前組內同事畫過一個流程圖,這里借用一下算是一個簡單的總結。

I/O的處理


I/O請求的處理時也是通過尋找相關聯的lextent,可能會存在空洞的情況,即讀到未寫過的數據,這部分就直接補零。

cloneextent共享

前面說到Bnode就是用來記錄共享的lextent,目前是在做完快照后對原卷進行寫I/O會觸發cow,從而產生clone操作。clone時就是將原對象的blobonode->blob_map移到onode->bnode->blob_map,並且將blob id置為負的,並設置共享標記,然后將新的快照對象的onode->bnode指向原對象的onode->bnode,並且用原onode里的lextents里的值賦給新的onodelextents,從而達到共享extent的目的,圖示僅供參考。

clone完之后,繼續對原對象進行寫I/O操作時,當碰到共享的blob時就需要跳過,新生成blob,並且取消對原來那部分lextent的引用,在后續的空間釋放時的判斷依據就是否還有引用。

小結

本文總體上介紹了bluestore的架構、相關元數據及內部I/O映射的邏輯,這還只是bluestore的冰山一角,后續會陸續對bluestore的處理流程、空間分配器、緩存管理、壓縮等實現進行分析。


免責聲明!

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



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