許多分佈式的存儲系統(好比Cassandra,Riak,HDFS,MongoDB,Kafka等)使用複製來保證數據的持久性。通常的,他們都是創建在JBOD(Just a Bunch of Disks)的配置之上——也就是說,沒有使用RAID來處理磁盤錯誤。若是集羣中的一個磁盤節點掛掉了,那麼磁盤上的數據就丟失了。爲了不永久性地丟失磁盤數據,數據庫系統保證了在其餘節點上的一些磁盤上保留了數據的拷貝。算法
常常用到的複製套路有三種:數據庫保證將每份數據都拷貝到三個不一樣計算機上的三個獨立的磁盤上。這麼作的緣由很簡單:磁盤只會在某個瞬間出現問題,若是一個磁盤掛掉了,你能夠是有時間去把它替換掉,而後你仍然能夠從你另外兩份拷貝中拿出一份來恢復數據並寫到新的磁盤上。在你恢復磁盤以前第二個磁盤也掛掉的機率過低了,你的磁盤在同一時刻都掛掉的可能性就如同小行星撞擊地球同樣微乎其微。數據庫
咱們還特意進行了計算,一個磁盤壞掉的可能性差很少是0.1%(可能比較粗略),兩個磁盤掛掉的可能性差很少是10的-6次方,三個磁盤同時掛掉的可能性大概是10的-9次方,也就是十億分之一。這個計算代表一個磁盤的出錯是獨立於其餘磁盤的出錯的——這不是很準確,舉個例子,若是你的磁盤都是從一個生產線上生產出來的,那麼它們有可能都是壞的——可是這對於咱們的想法來講已是足夠好了的。安全
迄今爲止,這看起來是合乎情理的,可是不幸的是這對於許多數據存儲系統都不是正確的。在這篇博客中我將向你們展現下這是爲何。ruby
丟失數據太容易了負載均衡
若是你的數據庫集羣僅僅包含三個機器,全部機器同時都掛掉的可能性實在過低了(排除相關的錯誤,好比一個數據中心被毀壞了)。然而一旦你使用了更大的集羣,那麼問題也相應地會發生變化。你在集羣中使用了更多的節點和磁盤,你丟失數據的可能性就會變大。分佈式
這是根據計算得來的,你可能會想「真的嗎?我已經把數據複製到三個磁盤上了。失敗的可能性怎麼會根據集羣的增大而變高呢。管集羣容量什麼事?」可是我計算了下可能性,並經過下面的圖標來向你講明緣由:函數
很明顯,這不是一個節點失敗的可能性——這是永久丟失全部三份拷貝數據的可能性,因此從備份中恢復數據只是保守的方法。你的集羣越大,你丟失數據的可能性就越大。這多是當你考慮爲複製數據支付時你可能不會想到的。設計
圖的Y軸有點隨意,依賴了不少設想,可是線條的方向是難以置信的。基於以前的設想,在某個時刻一個節點失敗的可能性是0.1%,然而圖上顯示的是,在一個具備8000個節點的集羣中,永遠失去一份數據的三個拷貝的機率大約是0.2%。是的沒錯,丟失全部三個拷貝的風險是丟失一個節點的數據的風險的兩倍。那麼這些拷貝用來作什麼呢?事件
從這幅圖上直覺地判斷:在一個擁有8000個節點的集羣中一些節點在某些時刻宕機的事件是時常發生的。這也許不是一個問題:必定機率的亂象和節點替換是能夠推斷的,還有一部分是例行的維護。然而,若是你很不幸,你複製的節點數據的目的節點都掛掉了,那麼你的數據就永遠找不回來了。數據的丟失牢牢是在集羣的總體數據集中比較小的部分,可是當你丟失了三份複製,你可能認爲「我確實不想丟失這些數據,」而不是「我沒想到偶然性地丟失了一些數據,儘管它們不是很大量的。」可能這部分丟失的數據是很重要的一部分數據。博客
全部三份複製都是壞節點的可能性要依賴於系統採用的複製算法。上圖僅僅依賴於數據被分紅必定數量的分區(或者叫分片),這樣每一個分區都存儲了三個隨機選擇的節點(或者叫作僞隨機哈希函數)。這是一致性哈希的特例,用在Cassandra和Riak(據我所知)。我不太肯定其餘系統是怎樣分配複製工做的,因此是從那些懂得多存儲系統的內部原理的人瞭解到。
計算丟失數據的可能性
讓我使用一個複製數據庫的機率模型向你展現我是怎樣計算上圖的。
假設一個獨立節點丟失數據的機率是p=P(節點損失)。我打算忽略在這個模型中的時間,簡要看下在某些時間段失敗的機率。舉個例子,咱們能夠假設p=0.001是某一天一個節點失敗的機率,花費一天時間去替換節點和將丟失數據轉儲到新的節點上也是合乎情理的。簡單來說,我並不想區分節點失敗和磁盤失敗,我只會把永久性失敗拿來講事兒。
設n爲集羣的節點數量。f爲失敗的節點數量(假設失敗是相對獨立的)是二項分佈的:
表達式是f個節點失敗的機率,表達式是保證n-f個節點不失敗的機率,是按照不一樣的方式從n中摘出f個節點的數量。讀做「n選擇f」,它被定義爲:
。。。。。。
具體的推導過程這裏也就不加詳細描述了。基於上面的公式,能夠推導出在一個具備n個節點,複製因子是(複製的備份節點數量)的集羣丟失一個或更多分區的機率。若是失敗的節點數f比複製因子要少,咱們能夠確信沒有數據丟失。然而,咱們須要增長全部的可能性,在f位於r和n之間的時候:
這稍微有些冗長,不過我認爲它是精確的。若是你讓r=3,p=0.001,k=256n,n在3和10000之間,而後你就能獲得上面那個圖。我寫了一些ruby程序去實現這個計算。
咱們使用了聯合綁定的到一個更簡單的猜測結果:
儘管一個分區的失敗不是徹底獨立於其餘分區的,這個猜測仍然適用。它好像更加接近實驗結果了:在途中,數據丟失機率更像是一條直線,和節點數量是成正比的。猜測代表機率是和數量成正相關的,咱們假設了每一個節點有固定的256個分區。
在實踐中。。。
在實踐中表現怎麼樣,我不太肯定。可是我認爲這是一個頗有意思的計算敏感現象。我據說過致使具備大型數據庫集羣的公司徽有真實數據丟失的狀況。可是文章和報道中倒不是很常見。若是你如今正在研究這方面的課題,能夠給我說下。
計算的結果代表:若是你想減小數據丟失的可能性,你應該減小分區數量,並增長複製因子。使用更多的備份花費更多,因此在考慮大型集羣時這一點已經花費很大了。然而,分區數量代表一個有意義的負載均衡處理。Cassandra原來是一個節點一個分區,可是後來變成了每一個節點256個分區來應對更好的負載分佈和高效的二次平衡。
你須要在這些起真正做用以前找到合理的大型集羣,可是上千級別的集羣是不少大型公司採用的。因此我很感興趣聽到在這個領域有實操經驗的人的講述。若是10000個節點天天永久丟失數據的機率控制在0.25%之內,那意味着一年有60%的數據都會丟失。
做爲一名分佈式數據系統的設計者看到了這篇文章以後有什麼想法?若是我講的是對的,設計複製方案時就應該加入更多的考慮。但願這片文章能夠提高你對現實的重視。由於3個複製節點確實不是那麼的安全。