crush資料分布演算法的全稱是:controlled, scalable, decentralized placement of replicated data.
開源的分布式儲存ceph採用crush資料分布演算法以達到以下幾個要求:
1. 資料分布均衡
2. 負載均衡
3. 靈活應對集群擴容和縮容:無論是新增或刪除裝置,都能最小化資料遷移
4. 支援大規模集群,消除因幾種儲存元資料而可能的單點失敗
crush演算法除了要達到以上幾個要求,它的主要目的是為了定位所儲存資料的位置。
那麼,crush演算法如何定位儲存資料的位置,並且滿足以上四大要求呢?
sage在2023年關於crush的原**寫得非常言簡意賅,並且主要是介紹抽象的crush演算法,而沒有結合ceph來談,比如就完全沒有提及pg和osd的概念。因此,估計是要看看sage的其他幾篇**和ceph的原始碼才能清楚一點。現根據前人的幾篇文獻總結如下。
crush演算法主要用於在ceph中定位儲存資料的位置,分為2個步驟:
1. 根據資料物件(object)的物件名,計算得到pg_id:
pg_id = pool_id + hash() % pg_num
2. 根據pg_id,計算得到一組osd
首先,為何是一組osd?
因為在ceph中,pool分為2種。第一種是replicated pool,比如每個object都有n個副本,就是屬於這種;第二種是ec pool,運用的是糾刪碼技術,每個object只有乙個副本,但有一些用於校驗和還原的資料塊。因此無論對於哪種pool,乙個object都需要多個osd來儲存其副本或者是儲存其糾刪碼,因此輸出是一組osd.
那麼,如何根據pg_id,計算得到一組osd呢?
首先,使用者在建立pool的時候,要設定儲存規則(placement rule)。當然,如果不設定,那麼就採用預設的規則。規則看起來是這樣的:
action resulting
take(root) root
select(1, row) row-2
select(3, cabinet) cab-21, cab-23, cab-24
select(1, disk) disk-210, disk-233, disk-245
emit
效果如下圖所示:
這三步(take、select(n,t), emit),是真正的原crush**中crush演算法的架構內容。而在涉及到select的時候,也需要介紹下是如何select的 -- 即bucket選擇演算法。詳情如下。
第一步take(root), 代表的是選中了這個root.
注意,在乙個ceph集群中,是有可能有多個root的,因為層次結構是使用者可自己設定的。比如,使用者把所有ssd的裝置設定成屬於乙個root,而把所有hdd的裝置設定成屬於另乙個root.
第二步,select(n, t). 這一步是關鍵,意思是,選擇n個型別為t的bucket.
那麼,是如何選擇的呢?
首先,select有2種操作方式:
choose firstn: 深度優先選擇出n個型別為t的子bucket,只到bucket為止
chooseleaf: 先選擇出n個型別為t的bucket,再在每個bucket下選擇乙個osd裝置
其次,就是如何去選擇bucket了,即bucket隨機選擇演算法。
一共有4種型別的bucket,對於每種型別的bucket,如何「選出」的方法是不一樣的。而在設定placement rule時,可以指定採用哪種bucket隨機選擇演算法,即屬於哪種bucket.
這裡首先介紹乙個重要的hash函式,因為該函式在多個bucket選擇演算法中都用到了,但作用各不相同:
hash(pg_id, r, bucket_id)
這個hash函式的輸入有3個:pg_id,r為 [1-n] 中的乙個值(n為副本數),bucket_id
這個hash函式的輸出是乙個 [0-1] 之間的數值
1. uniform bucket
每個bucket的權重都相同(這裡應該指的是同層級的節點的weight都相同),並且基本沒有硬體裝置新增和刪除的情況
這種bucket因為所有item權重都相同,因此選擇速度很快,為o(1),但是一旦需要新增或刪除裝置,就退化成普通hash,即幾乎所有資料都需要遷移。在實際中,一般不會使用這種bucket.
2. list bucket
其子item在記憶體中使用資料結構中的鍊錶來儲存,其所包含的item可以具有任意權重。(注:此處item和bucket是乙個意思) 具體查詢bucket的方法是:
1> 從表頭item開始查詢,先得到表頭item的權重wh,剩餘鍊錶中所有item權重之和為ws.
2> 根據 hash(pg_id, r, bucket_id) 計算得到乙個 [0-1] 之間的值v,若v在 [0-wh/ws) 的範圍,則選擇表頭item,並返回表頭item的id;
3> 否則,繼續遍歷剩餘的鍊錶,繼續上述的遞迴查詢。
由以上分析可知,list bucket的查詢複雜度為 o(n)
這種bucket在實際中一般也不太會用,因為其刪除硬體時,效率也較差。
3. tree bucket
此種型別的bucket的子item組織成樹的結構。每個osd是葉子節點;根節點和中間節點是虛擬節點,其權重等於左右子樹的權重之和。具體查詢bucket的方法如下:
1> 從根節點開始遍歷
2> 設左子樹權重為w1,而當前節點權重為wn,然後根據 hash(pg_id, r, bucket_id) 計算得到乙個 [0-1] 之間的值v;
a> 若v在 [0-w1/wn) 之間,那麼在左子樹中繼續選擇item;
b> 否則在右子樹中選擇item
c> 一直遍歷,直至葉子節點
由以上分析可知,tree bucket的查詢複雜度為 o(log n)
這種bucket在實際中可以考慮使用。其選擇速度是很快的,而在新增和刪除硬體裝置時,速度也還可以。
4. straw bucket
此種型別的bucket選擇演算法是ceph的預設選擇演算法。具體如下:
1> 函式 f(wi) 是和item的權重wi相關的函式,決定了每個item被選中的概率;權重(weight)越高的item則被選中的概率越大。
注意,這裡的weight並不是相當於磁碟可用空間,而是相當於總空間,因此是固定不變的。
2> 給每個item計算乙個長度
length = f(wi) * hash(pg_id, r, bucket_id)
然後選擇length最大的item.
因為straw在增刪裝置時的表現最佳,而在選擇速度上也還可以,因此,它被選作預設的bucket選擇演算法。
以上各個bucket選擇演算法的對比情況見下表:
bucket選擇演算法
選擇的速度
item新增的容易程度
item刪除的容易程度
uniform
o(1)
poor
poor
list
o(n)
optimal
poor
tree
o(log n)
good
good
straw
o(n)
better
better
straw2
o(n)
optimal
optimal
當選出乙個osd後,可能出現衝突(重複選擇)、失效(磁碟損壞)、過載的情況,那麼此時就重新選擇一次。對於replicated pool和ec pool,重新選擇的演算法是略有不同的。replicated pool是直接再選乙個即可(r' = r + f,f為總失敗的次數),而ec pool則需令r值增加n的倍數後(r' = fr*n, fr為在這個副本上失敗的次數),再運用hash(pg_id, r', bucket_id)來進行重新選擇。
下圖顯示了兩種pool在失敗的情況下是如何決定r'的值:
最後一步,第三步,emit,即輸出結果。所謂結果,即一組osd.
1. 《ceph原始碼分析》第四章 crush資料分布演算法
2. crush: controlled, scalable, decentralized placement of replicated data
3. 大話ceph - crush那點事兒
4. ceph剖析:資料分布之crush演算法與一致性hash
Ceph學習筆記(2) CRUSH資料分布演算法
分布式儲存系統需要讓資料均勻的分布在集群中的物理裝置上,同時在新裝置加入,舊裝置退出之後讓資料重新達到平衡狀態尤為重要。新裝置加入後,資料要從不同的老裝置中遷移過來。老裝置退出後,資料遷移分攤到其他裝置。將檔案 塊裝置等資料分片,經過雜湊,然後寫入不同的裝置,從而盡可能提高i o併發與聚合頻寬。在實...
A 演算法介紹
不得不嘆服,強大演算法背後,都是簡單得不能再簡單的邏輯。普林斯頓的演算法課程作業裡,要讓用a 演算法。什麼都沒接觸到過,看到後有種想哭的感覺!於是網上查閱資料,漸漸的明白了怎麼回事。通過對a 演算法的學習的個人感悟 計算乙個代價函式,評估每一步的代價,並找到代價最小的方向。最終得到的解可能不是最優,...
A 演算法介紹
參考 在最短路徑演算法中 dijkstra演算法 每次貪心選擇當前更新的點中距離最近的進行更新。貪心演算法 每次選擇離終點評估最近的點進行更新 a 演算法 綜合了dijkstra和貪心,相當於即考慮了起點到此點的距離,又評估了此點到終點的距離。a 演算法通過下面這個函式來計算每個節點的優先順序。其中...