CRUSH算法,全稱Controlled Replication Under Scalable Hashing (可擴展哈希下的受控複製),它是一個可控的、可擴展的、分佈式的副本數據放置算法, 經過CRUSH算法來計算數據存儲位置來肯定如何存儲和檢索數據。算法
保障數據分佈的均衡性後端
讓數據可以均勻的分不到各個節點上面,同時讓數據訪問的讀寫操做在各個節點和磁盤上保持負載均衡。緩存
集羣的靈活伸縮性服務器
能夠方便快速的增長或刪除節點,對失效的節點自動檢測處理,可以自動實現數據的均衡,而且儘量少的遷移改動數據。網絡
支持更大規模的集羣架構
可以作到數據分佈算法維護較小的元數據與計算量, 隨着集羣規模的不斷增長,保持較小的數據分佈式算法開銷,不能呈線性增加。負載均衡
PG到OSD的映射的過程算法稱爲CRUSH 算法,它是一個僞隨機的過程,能夠從全部的OSD中,隨機性選擇一個OSD集合,可是同一個PG每次隨機選擇的結果是不變的,實質上映射的OSD集合是固定的。框架
CRUSH使Ceph客戶機可以直接與OSDs通訊,而不是經過集中的服務器或代理。經過算法肯定的數據存儲和檢索方法,從而避免了單點故障、性能瓶頸和對其可伸縮性的物理限制。異步
Crush Map將系統的全部硬件資源描述成一個樹狀結構,而後再基於這個結構按照必定的容錯規則生成一個邏輯上的樹形結構,樹的末級葉子節點device也就是OSD,其餘節點稱爲bucket節點,根據物理結構抽象的虛擬節點,包含數據中心抽象、機房抽象、機架抽象、主機抽象。分佈式
Ceph的存儲結構
Ceph爲了保存對象,會先構建一個池(pool),把pool能夠比喻成一個倉庫,一個新對象的保存就相似於把一個包裹放到倉庫裏面。
爲了更好的提高效率,Pool能夠劃分爲若干的PG(Placement Group)歸置組,這相似於倉庫裏面有不一樣的儲物架,全部的儲物架組成了一個完整的倉庫,全部的PG也就構建成了一個pool。
PG的分配存儲
對象是如何保存至哪一個PG上?假設Pool名稱爲rbd,共有256個PG,每一個PG編個號分別叫作0x0, 0x1, 0x2,... 0xFF。 具體該如何分配?這裏能夠採用Hash方式計算。
假設有兩個對象名, 分別爲bar和foo的,根據對象名作Hash計算:
HASH(‘bar’) = 0x3E0A4162
HASH(‘foo’) = 0x7FE391A0
經過Hash獲得一串隨機的十六進制的值, 對於一樣的對象名,計算出的結果可以永遠保持一致,但咱們預分配的是256個PG,這就須要再進行取模處理, 所得的結果會落在【0x0,0xFF】區間:
0x3E0A4162 % 0xFF ===> 0x62
0x7FE391A0 % 0xFF ===> 0xA0
實際在Ceph中, 存在不少個Pool,每一個Pool裏面存在若干個PG,若是兩個Pool裏面的PG編號相同,該如何標識區分?Ceph會對每一個pool再進行編號,好比上面名稱爲rbd的Pool,給定ID編號爲0, 再新增一個Pool,ID編號設定爲1,那麼一個PG的實際編號是由pool_id + . + pg_id組成。由此推論, 剛纔的bar對象會保存在編號爲0的pool,pg編號爲62裏面。
OSD的分配存儲
Ceph的物理層,對應的是服務器上的磁盤,Ceph將一個磁盤或分區做爲OSD,在邏輯層面,對象是保存至PG內,如今須要打通PG與OSD之間的聯繫, Ceph當中會存在較多的PG數量,如何將PG平均分佈各個OSD上面,這就是Crush算法主要作的事情: 計算PG -> OSD的映射關係。
上述所知, 主要兩個計算步驟:
POOL_ID(對象池) + HASH(‘對象名稱’) % pg_num(歸置組)==> PG_ID (完整的歸置組編號)
CRUSH(PG_ID)==> OSD (對象存儲設備位置)
爲何須要採用Crush算法
若是把CRUSH(PG_ID)改爲 HASH(PG_ID)% OSD_NUM 可否適用? 是會存在一些問題。
1)若是掛掉一個OSD,全部的OSD_NUM 餘數就會發生變化,以前的數據就可能須要從新打亂整理, 一個優秀的存儲架構應當在出現故障時, 可以將數據遷移成本降到最低, CRUSH則能夠作到。
2)若是增長一個OSD, OSD_NUM數量增大, 一樣會致使數據從新打亂整理,可是經過CRUSH能夠保障數據向新增機器均勻的擴散, 且不須要從新打亂整理。
3)若是保存多個副本,就須要可以獲取多個OSD結果的輸出, 可是HASH方式只能獲取一個, 可是經過CEPH的CRUSH算法能夠作到獲取多個結果。
Crush算法如何實現
每一個OSD有不一樣的容量,好比是4T仍是800G的容量,能夠根據每一個OSD的容量定義它的權重,以T爲單位, 好比4T權重設爲4,800G則設爲0.8。
那麼如何將PG映射到不一樣權重的OSD上面?這裏能夠直接採用CRUSH裏面的Straw抽籤算法,這裏面的抽籤是指挑取一個最長的籤,而這個籤值得就是OSD的權重。若是每次都存儲在容量最大的OSD上,很容易將該節點塞滿, 這就須要採起相似隨機權重的算法來作實現。
![file](/img/bVcQ9tP)
主要步驟:
計算HASH: CRUSH_HASH( PG_ID, OSD_ID, r ) ==> draw
把r當作一個常數,將PG_ID, OSD_ID一塊兒做爲輸入,獲得一個HASH值。
增長OSD權重: ( draw &0xffff ) * osd_weight ==> osd_straw
將計算出的HASH值與OSD的權重放置一塊兒,這樣就可以獲得每一個OSD的籤長, 權重越大的,數值越大。
Crush目的是隨機跳出一個OSD,而且要知足權重越大的OSD,挑中的機率越大,爲了保障隨機性,將每一個OSD的權重都乘以一個隨機數也就是HASH值,再去結果最大的那個。若是樣本容量足夠大, 隨機數對選中的結果影響逐漸變小, 起決定性的是OSD的權重,OSD的權重越大, 被挑選的機率也就越大,這樣可以作到數據的有效分佈。
Crush所計算出的隨機數,是經過HASH得出來,這樣能夠保障相同的輸入, 會得出一樣的輸出結果。 因此Crush並非真正的隨機算法, 而是一個僞隨機算法。
這裏只是計算得出了一個OSD,在Ceph集羣中是會存在多個副本,如何解決一個PG映射到多個OSD的問題?
將以前的常量r加1, 再去計算一遍,若是和以前的OSD編號不同, 那麼就選取它;若是同樣的話,那麼再把r+2,再從新計算,直到選出三個不同的OSD編號。
![file](/img/bVZkcX)
假設常數r=0,根據算法(CRUSH_HASH & 0xFFFF) * weight 計算最大的一個OSD,結果爲osd.1的0x39A00,也就是選出的第一個OSD,而後再讓r=1, 生成新的CRUSH_HASH隨機值,取得第二個OSD,依次獲得第三個OSD。
步驟:
網絡通訊框架三種不一樣的實現方式:
Simple線程模式
Async事件的I/O多路複用模式
XIO方式使用了開源的網絡通訊庫accelio來實現
消息的內容主要分爲三部分:
user data //須要發送的實際數據
步驟:
osd寫入過程:
問題:
故障檢測時間和心跳報文帶來的負載, 如何權衡下降壓力?
故障檢測策略應該可以作到:
及時性:節點發生異常如宕機或網絡中斷時,集羣能夠在可接受的時間範圍內感知。
適當的壓力:包括對節點的壓力,和對網絡的壓力。
容忍網絡抖動:網絡偶爾延遲。
擴散機制:節點存活狀態改變致使的元信息變化須要經過某種機制擴散到整個集羣。
OSD節點會監聽public、cluster、front和back四個端口
Ceph OSD之間相互心跳檢測
本文由mirson創做分享,如需進一步交流,請加QQ羣:19310171或訪問www.softart.cn