自:http://my.oschina.net/JJREN/blog/51548
上一次,我們介紹了幾種常見的kv存儲模型,下面我們就正式進入到分佈式存儲的場景裏去看看這套東西在分佈式場景下的運作方式吧。
/algorithm/20120106/317132.html 上一篇
在分佈式key-value中,很多原來的知識是可以繼續複用的。因爲k-v解決的問題實在是非常的簡單,只不過是根據一個key找到value的過程,所以原來的知識,現在也繼續的可以用。
但有兩個額外的因素需要考慮
網絡延遲
TCP/IP –公用網絡,ip跳轉慢,tcp包頭大
可能出現不可達問題
這其實是狀態機同步中最難的一個問題,也就是,A給B消息,B可能給A的反饋可能是:1. 成功 2. 失敗 3. 無響應。最難處理的是這個無響應的問題。以前的文章中我們討論過這個問題,以後還會碰到。這裏暫且hold住。
先上圖一張,在未來的幾周內,我們都會依託這張圖來解釋分佈式K-V系統
可以看到,在客戶機到服務器端,有這麼幾個東西
一,規則引擎
二,數據節點間的同步
抽象的來看,分佈式K-V系統和傳統的單機k-v系統的差別,也就只在於上面的兩個地方的選擇。
今天先來談規則引擎
抽象的來看,規則引擎面向的場景應該被這樣的描述:對於有狀態的數據,需要一套機制以保證其針對同一個數據的多次請求,應該物理上被髮送到同一個邏輯區塊內的同一個數據上。
舉例子來說,一個人進行了三筆交易,每筆交易都是這個人給其他人100元。那麼,這三比交易的更新(update set money = money – 100 where userid = ?) 必須被髮到同一臺機器上執行,才能拿到正確的結果。【不考慮讀性能的gossip模型除外】
這種根據一個userid 找到其對應的機器的過程,就是規則引擎所要處理的事情。
我們對於規則引擎的需求,一般來說也就是要查的快,第二個是要能儘可能的將數據平均的分配到所有的節點中去。第三個,如果新的節點加入進去,希望能夠只移動那些需要移動的數據,不需要移動的數據則不要去移動他。
那麼一想到“根據xxx找到xxx”相信大家第一個想到的一定又是以前說過的K-V了。所以我們就再複習一遍: )
Hash
O(1)效率
不支持範圍查詢(時間這樣的查詢條件不要考慮了)
不需要頻繁調整數據分佈
順序
主要是B-Tree
O(logN)效率
支持範圍查詢
需要頻繁調整節點指針以適應數據分佈
這也就是我們最常用的兩種分佈式k-value所使用的數據結構了
首先來看HASH的方法。Hash其實很容易理解,但我跟不少人交流,發現大家可能對一致性hash的理解有一定的誤解。下面請允許我給大家做個簡單的介紹。
簡單取模:
最簡單的HASH 就是取mod,user_id % 3 。這樣,會將數據平均的分配到0,1,2 這三臺機器中。
這也是我們目前最常用的,最好用的方案。但這套方案也會有一個問題,就是如果id % 3 -> id % 4 總共會有80%的數據需要變動hash桶,想一想,只增加了一臺機器,但80%的數據需要從一個機器移動到另外一個機器,這無疑是不夠經濟的,也是對遷移不友好的方案。
不過,雖然增加一臺機器,會發生無謂的數據移動,但取模的方案在一些特殊的場景下,也能很好的滿足實際的需要,如id % 3 -> id % 6,這種情況下,只需要有50%的數據移動到新的機器上就可以了。這也就是正常的hash取模最合適的擴容方式—–> 倍分擴容。我們一般把這種擴容的方式叫做”N到2N”的擴容方案。
取模Hash還有個無法解決的問題,就是無法處理熱點的問題,假設有一個賣家有N個商品。如果按照賣家ID進行切分,那麼就有可能會造成數據不均勻的問題。有些賣家可能有10000000個商品,而有些賣家只會有10個。這種情況下如果有大量商品的賣家針對他的商品做了某種操作,那這樣無疑會產生數據熱點。如何解決這類問題,也是分佈式場景中面臨的一個重要的問題。
既然簡單取模有這麼多的問題,那有沒有辦法解決這些問題呢?
首先,我們來介紹第一種解決這個問題的嘗試。
一致性Hash.
先來個圖,這套圖估計幾乎所有對Nosql稍有了解的人都應該看過,在這裏我會用另外的方式讓大家更容易理解
上面這個圖,用代碼來表示,可以認爲是這樣一套僞碼
Def idmod = id % 1000 ;
If(id >= 0 and id < 250)
returndb1;
Else if (id >= 250 and id < 500)
returndb2;
Else if (id >= 500 and id < 750)
returndb3;
Else
returndb4;
這個return db1 db2 db3 db4 就對應上面圖中的四個淺藍色的點兒。
而如果要加一個node5 ,那麼僞碼會轉變爲
Def idmod = id % 1000 ;
If(id >= 0 and id < 250)
returndb1;
Else if (id >= 250 and id < 500)
returndb2;
Else if (id >= 500 and id < 625)
returndb5;
else if(id >= 625 and id <750)
returndb3
Else
returndb4;
從這種結構的變化中,其實就可以解決我們在普通hash時候的面臨的兩個問題了。
1.可以解決熱點問題,只需要對熱點的數據,單獨的給他更多的計算和存儲資源,就能部分的解決問題(但不是全部,因爲遷移數據不是無成本的,相反,成本往往比較高昂)
2.部分的能夠解決擴容的問題,如果某個點需要加機器,他只會影響一個節點內的數據,只需要將那個節點的數據移動到新節點就可以了。
但一致性hash也會帶來問題,如果數據原本分佈就非常均勻,那麼加一臺機器,只能解決臨近的一個節點上的熱點問題,不會影響其他節點,這樣,熱點擴容在數據分佈均勻的情況下基本等於n->2n方案。因爲要在每個環上都加一臺機器,才能保證所有節點的數據的一部分遷移到新加入的機器上。
這無疑對也會浪費機器。
於是,我們又引入了第三套機制:
虛擬節點hash
Def hashid = Id % 65536
可以很容易的看出,上面這套虛擬節點的方案,其實與id % 4的結果等價。
可以認爲一致性hash和普通節點hash,都是虛擬節點hash的特例而已。
使用虛擬節點hash,我們就可以很容易的解決幾乎所有在擴容上的問題了。
碰到熱點?只需要調整虛擬節點map中的映射關係就行了
碰到擴容?只需要移動一部分節點的映射關係,讓其進入新的機器即可
可以說是一套非常靈活的方案,但帶來的問題是方案有點複雜了。
所以,我們一般在使用的方式是,首先使用簡單的取模方案,如id % 4。在擴容的時候也是用N->2N的方案進行擴容。但如果碰到需求複雜的場景,我們會“無縫”的將業務方原來的簡單取模方案,直接變爲使用虛擬節點hash的方案,這樣就可以支持更復雜的擴容和切分規則,又不會對業務造成任何影響了。
好,到這,我基本上就給大家介紹瞭如何使用Hash來完成分佈式k-value系統的規則引擎構建了。
下一期我們來看一下使用樹的方案,當然,主要也就是hbase這個東西了,可能會再介紹一下mongodb的自動擴容方案。