1. 程式人生 > >Ceph Crush演算法詳解

Ceph Crush演算法詳解

Ceph作為最近關注度比較高的統一分散式儲存系統,其有別於其他分散式系統就在於它採用Crush(Controlled Replication Under Scalable Hashing)演算法使得資料的儲存位置都是計算出來的而不是去查詢專門的元資料伺服器得來的。另外,Crush演算法還有效緩解了普通hash演算法在處理儲存裝置增刪時帶來的資料遷移問題。接下面我會分三篇博文介紹這個重量級的演算法,第一篇主要回答三個問題:什麼是CRUSH?它能做什麼?它是怎麼工作的?第二篇主要介紹CRUSH中一個重要的概念Bucket。第三篇主要介紹如何通過CRUSH控制資料遷移。

1. 什麼是CRUSH?

隨著大規模分散式系統的出現,系統必須能夠平均的分佈資料和負載,最大化系統的利用率,並且能夠處理系統的擴充套件和系統故障。一般的分散式系統都會採用一個或者多箇中心服務用來控制資料的分佈,這種機制使得每次IO操作都會先去一個地方查詢資料在叢集中的元資料資訊。當叢集的規模變大或者系統的workload比較大時,這些中心伺服器必然會成為效能上的瓶頸。Ceph摒棄了這種做法,而是通過引入CRUSH演算法,將資料分佈的查詢操作變成了計算操作,並且是在client端完成。

CRUSH是受控複製的分散式hash演算法,是ceph裡面用於控制資料分佈的一種方法,能夠高效穩定的將資料分佈在普通的結構化的叢集中。它是一種偽隨機的演算法,在相同的環境下,相似的輸入得到的結果之間沒有相關性,相同的輸入得到的結果是確定的。它只需要一個叢集的描述地圖和一些規則就可以根據一個整型的輸入得到存放資料的一個裝置列表。Client在有IO操作的時候,可能會執行CRUSH演算法。

2. CRUSH能幹什麼?

如上面介紹的,它主要是ceph用來控制資料分佈的。通過下圖,我們看一下它在整個IO流程中處於什麼位置。

Ceph的後端是一個物件儲存(RADOS),所以所有的資料都會按照一個特定的size(ceph系統預設是4M)被切分成若干個物件,也就是上面的Objects。每一個Object都有一個Objectid(oid),Objectid的命名規則是資料所在image的block_name_prefix再跟上一個編號,這個編號是順序遞增的。通過(poolid, hash(oid) & mask),每個object都可以得到它對應的pgid。有了這個pgid之後,Client就會執行CRUSH演算法得到一個OSD列表(OSD1,OSD2,OSD3)。然後對它們進行篩選,根據副本數找出符合要求的OSD,比如OSD不能是failed、overloaded的。知道資料要存在哪些OSD上之後,Client會向列表中的第一個OSD(primary osd)發起IO請求。然後這個OSD按照讀寫請求分別做相應的處理。

所以一句話說明CRUSH的作用就是,根據pgid得到一個OSD列表。

3. CRUSH是如何工作的?

CRUSH是基於一張描述當前叢集資源狀態的map(Crush map)按照一定的規則(rules)得到這個OSD列表的。Ceph將系統的所有硬體資源描述成一個樹狀結構,然後再基於這個結構按照一定的容錯規則生成一個邏輯上的樹形結構作為Crush map。數的葉子節點是OSD。

3.1 Crush map

一個Crush map一般包括四個部分:

1)配置引數

  1. # begin crush map  
  2. tunable choose_local_tries 0  
  3. tunable choose_local_fallback_tries 0  
  4. tunable choose_total_tries 50  
  5. tunable chooseleaf_descend_once 1  
  6. tunable straw_calc_version 1  
2)裝置,比如說我有一個三個osd組成的ceph環境,那麼裝置就有三個
  1. # devices  
  2. device 0 osd.0  
  3. device 1 osd.1  
  4. device 2 osd.2  
3)bucket型別。
  1. # types  
  2. type 0 osd  
  3. type 1 host  
  4. type 2 chassis  
  5. type 3 rack  
  6. type 4 row  
  7. type 5 pdu  
  8. type 6 pod  
  9. type 7 room  
  10. type 8 datacenter  
  11. type 9 region  
  12. type 10 root  
ceph通過bucket這個邏輯概念來描述這個Crush map樹形結構的中間節點。bucket就像是一個容器,一箇中間節點就是一個bucket,它的孩子節點就是bucket裡包含的items。
4)bucket的具體定義
  1. root default {  
  2.         id -1           # do not change unnecessarily  
  3.         # weight 3.000  
  4.         alg straw  
  5.         hash 0  # rjenkins1  
  6.         item rack1 weight 3.000  
  7. }  
id:中間節點的bucket id都是負數,這個主要是和具體的裝置相區別的。
weight:bucket的權重
alg:bucket的型別
hash:bucket中使用到的hash演算法
item:bucket裡包含哪些元素

3.2 Rules

有了Crush map之後,如何一步一步從bucket中選出元素,這個就是有rule來定義的。一個rule就是一系列的操作,一般一個pool都會對應有一個rule。
一個rule的定義是這樣的:

  1. rule replicated_ruleset {  
  2.         ruleset 0  
  3.         type replicated  
  4.         min_size 1  
  5.         max_size 10  
  6.         step take default  
  7.         step choose firstn 0 type osd  
  8.         step emit  
  9. }  
ruleset:id,表明這個rule是屬於這個ruleset的。
type:表明這個rule在哪使用,storage drive (replicated) or a RAID。
min_size和max_size用來限定這個rule的使用範圍,即當一個pool的副本數小於min_size或者大於max_size的時候不使用這個rule。
step開頭的就是三個操作。take是選擇一個bucket,然後從這個bucket開始往下遍歷,找出OSD。choose是從bucket中找出若干個type型別的項,還有一個chooseleaf操作是bucket中選出若干個leaf節點。至於個數是由firstn後面的數字指定的。如果是0,就按照副本數選擇,如果是正數,就按個數來,如果是負數,就按副本數+負數得到的值來選。最後一步是emit,就是輸出結果。

下面是sage論文上的一個例子:

rule和對應的結果是:

  1. take(root)          root  
  2. select(1, row)          row2  
  3. select(3, cabinet)      cab21, cab23, cab24  
  4. select(1, disk)         disk2107, disk2313, disk2437  
  5. emit  
3.3 Crush map中各個bucket權重的計算方法
Crush演算法按照rule從bucket中選擇item的時候,一般和bucket的權重有關係,每個bucket的權重是所有孩子的權重的和。所以權重是按照自下往上的順序,依次計算的,也就是先算出osd的權重,然後再算出它父輩的權重。OSD的權重和它的容量有關係,現在的約定是1T容量的權重是1,OSD的權重就是它的容量/1T。