Ceph bluestore中的緩存管理

從15年3月接觸Ceph分佈式存儲系統,至今已經5年了;因爲工作的需要,對Ceph的主要模塊進行了較深入的學習,也在Ceph代碼層面做了些許改進,以滿足業務需要(我們主要使用M版本)。最近得閒,將過往的一些學習心得、改進以及優化思路記錄下了,希望能對後來者有所幫助。

這是第四篇:Ceph bluestore中的緩存管理

前言

bluestore構建在裸盤上,在用戶空間,通過FreelistManager以及Allocator來管理磁盤空間且通過direct-io訪問磁盤,所以它不能夠利用到系統的pagecache。爲了提升性能,bluestore在用戶態實現了基於2q和lru算法的cache。

bluestore中的緩存管理

緩存對象及相關數據結構

在繼續後文前,我們先來明確幾個主要的結構及其作用:

  1. Object: Ceph實際上分佈式對象存儲系統,通過Object來表示存儲系統中的IO對象,類似傳統文件系統中的文件,由數據(Bufferlist)和元數據(Onode)兩部分組成
  2. Collection:pg的內存表示,常駐內存,它是Object對象的邏輯倉庫
  3. bluestore_cnode_t:Collection的元數據
  4. Onode: Object的元數據,包含對象id以及指向對象數據邏輯位置<offset, len>的ExtentMap結構
  5. bluestore_onode_t:Onode的元數據,存儲在db中
  6. Cache:bluestore 緩存對象,有LRUCache和TwoQCache兩種
  7. OnodeSpace:Onode的緩存管理對象,包含一個Cache對象以及一個unordered_map類型的Onode映射
  8. Buffer:Object對象數據(bufferlist,offset,len)的緩存表示
  9. BufferSpace:用於管理Buffer,包含一個Buffer的map,角色類似OnodeSpace

Collection與bluestore_cnode_t, Onode與bluestore_onode_t的關係類似於Linux文件系統中內存inode與磁盤inode的關係

下面的UML類圖展示了上述對象之間的關係:
在這裏插入圖片描述
OSD啓動時,Bluestore會創建多個Cache實例,之後從db加載Collection到內存中時,通過cid % shard_num模與Cache關聯,所有的Collection常駐內存,通過一個map來管理;每個Collection包含一個Cache指針,一個元數據對象bluestore_cnode_t以及一個OnodeSpace類型的onode_map變量,這個變量包含與Collection相同的Cache實例以及一個Onode的unorderd_map對象(這個unorderd_map應該是用來加速查找的,因爲Cache可能被多個Collection共享,通過每個OnodeSpace中的專屬結構來管理本Collection的Onode,可以提升效率)。

Ceph中的對象元數據(這裏說的元數據是指數據元數據,對象屬性通過ObjectMap來管理)通過Onode來管理,包含對象的id以及一個ExtentMap結構來表示對象的數據偏移及長度信息,每個Extent表示邏輯上連續的數據片段,通過<offset, len>來標識;由於一段邏輯數據在物理上可能不連續,可能對應多個物理存儲區bluestore_pextent_t, 爲便於管理引入了Blob,它是bluestore_pextent_t的集合(PExtentVector),Blob還是Ceph中的數據共享單位,快照克隆的時候,共享數據通過更新引用計數即可,不需要磁盤拷貝。

Bluestore提供了LRU cache 和 2Q cache兩種緩存機制,默認採用2Q cache。LRUCache,淘汰最近最少使用的item,採用list來實現,Onode和Buffer各佔用一個隊列;TwoQCache,針對Onode和Buffer採用不同的處理方式,其中Onode緩存採用LRU實現,而Buffer將整個緩存空間分爲三部分,使用三個隊列,分別稱爲hot,warm_in,warm_out來管理,新加入的數據加入到warm_in隊列,再次訪問的數據加入到hot隊列,warm_in中的冷數據向移到warm_out隊列,另外TwoQCache充分利用局部性原理,加入對象時會根據offset匹配前後的對象,將新對象加入到臨近對象的前面。下圖是LRU和2Q緩存的一個示例說明:
在這裏插入圖片描述

緩存管理

緩存系統的基本操作包括CRUD四種,Cache主要提供了:add(C),touch(U),rm(D),trim(D)四個方法,OnodeSpace或者BufferSpace提供了:add(C),rename(U),lookup(R),discard (U),remove(D)四個方法, 可以想到OnodeSpace和BufferSpace實現了一部分業務語義:

add方法:包括Onode和Buffer兩部分,就Onode而言:LRUCache和TwoQCache採用相同的策略,如果對象已經在緩存中,則直接返回,否則將Onode加入到OnodeSpace的onode_map中並插入到cache的onode隊列的頭部;對於Buffer:LRUCache採用與Onode相同的策略,將對象插入到cache的buffer隊列的頭部,而TwoQCache維護了三個隊列,根據熱度屬性將對象分別添加到hot,warm_in,warm_out隊列。
lookup方法:用來從OnodeSpace的onode_map中查找對象,如果找到則通過touch方法將對象提升到cache的onode隊列的頭部,需要注意的是TwoQCache中,只有hot隊列中的隊列纔會提升到cache的頭部。
rm方法:包括Onode和Buffer兩部分,就Onode而已:LRUCache和TwoQCache採用相同的策略,將對象從隊列中刪除,將對象從OnodeSpace的onode_map中刪除;對於Buffer:LRUCache採用與Onode相同的策略,將對象從隊列中刪除,TwoQCache則將對象從hot,warm_in,warm_out之一中移除。
trim方法:根據需要保留的onode_max以及buffer_max清理緩存,包括Onode和Buffer兩部分,基本上就是簡單的將對象從隊列中移除,TwoQCache對Buffer的處理有些許不同:它根據各隊列的數據空間佔比(ratio)來計算每個隊列需要移除的對象。
rename方法:處理對象的重命名操作。
discard方法:淘汰給定區間的緩存對象。

緩存的使用

下文從緩存初始化,讀IO和寫IO三個方面來分析Bluestore cache的使用。

  1. 緩存的初始化
    1)OSD init初始化時 Bluestore根據設置的shard_num, cache_type創建cache實例
    2)Bluestore mount時從db加載Collection,通過cid與 shard_num取模關聯特定的cache實例,Blustore通過一個map來管理內存中的Collection。
  2. 讀io緩存使用
    1)Bluestore收到讀請求後,從cache中獲取對象的onode(包含extent信息),並且將onode移到隊列的頭部
    2)如果上述獲取的onode的extent中不全部包含<offset, len>所需要的信息,則從db中加載缺失部分的extent
    3)根據<offset, len>從cache中讀取緩存數據,並將命中的數據移到隊列頭部
    4)對於不在緩存中的數據則從後端磁盤上讀取,如果啓用了bufferIO,則將數據加入到緩存隊列頭部(warm_in隊列)
  3. 寫io緩存使用
    1)Bluestore收到寫請求後,從cache中獲取onode或者創建一個onode, 並將onode插入隊列的頭部
    2)如果上述獲取的onode的extent中不全部包含<offset, len>所需要的信息,則從db中加載缺失部分的extent
    3)如果啓用了bufferIO,則將數據(如果IO區間有重疊會先從磁盤讀取數據做合併)加入到緩存隊列的頭部
    4)(在執行io前)將緩存元數據(Onode,SharedBlob)持久化到db中(這個過程中,ExtentMap可能需要分片,根據needs_reshard_begin/needs_reshard_end來決定需要reshard的區間)。

步驟3中有幾點需要注意:

  1. 數據緩存是以Blob單位進行的,
  2. 如果和之前的緩存數據有部分重疊,會truncate掉front和tail,如果完全包含則先從cache中刪除掉
  3. 根據數據的熱度屬性將對象添加到不同的隊列中,新增的數據插入到warm_in隊列,如果與之前的數據有重疊,那麼加入哪個隊列與之前數據的熱度屬性相關
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章