我的Java路上那些事兒

          快樂成長(zhǎng)
          posts - 110, comments - 101, trackbacks - 0, articles - 7
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理
            一致性哈希算法是分布式系統(tǒng)中常用的算法。比如,一個(gè)分布式的存儲(chǔ)系統(tǒng),要將數(shù)據(jù)存儲(chǔ)到具體的節(jié)點(diǎn)上,如果采用普通的hash方法,將數(shù)據(jù)映射到具體的節(jié)點(diǎn)上,如key%N,key是數(shù)據(jù)的key,N是機(jī)器節(jié)點(diǎn)數(shù),如果有一個(gè)機(jī)器加入或退出這個(gè)集群,則所有的數(shù)據(jù)映射都無效了,如果是持久化存儲(chǔ)則要做數(shù)據(jù)遷移,如果是分布式緩存,則其他緩存就失效了。

              因此,引入了一致性哈希算法:


           

          把數(shù)據(jù)用hash函數(shù)(如MD5),映射到一個(gè)很大的空間里,如圖所示。數(shù)據(jù)的存儲(chǔ)時(shí),先得到一個(gè)hash值,對(duì)應(yīng)到這個(gè)環(huán)中的每個(gè)位置,如k1對(duì)應(yīng)到了圖中所示的位置,然后沿順時(shí)針找到一個(gè)機(jī)器節(jié)點(diǎn)B,將k1存儲(chǔ)到B這個(gè)節(jié)點(diǎn)中。

          如果B節(jié)點(diǎn)宕機(jī)了,則B上的數(shù)據(jù)就會(huì)落到C節(jié)點(diǎn)上,如下圖所示:


           

          這樣,只會(huì)影響C節(jié)點(diǎn),對(duì)其他的節(jié)點(diǎn)A,D的數(shù)據(jù)不會(huì)造成影響。然而,這又會(huì)造成一個(gè)“雪崩”的情況,即C節(jié)點(diǎn)由于承擔(dān)了B節(jié)點(diǎn)的數(shù)據(jù),所以C節(jié)點(diǎn)的負(fù)載會(huì)變高,C節(jié)點(diǎn)很容易也宕機(jī),這樣依次下去,這樣造成整個(gè)集群都掛了。

                 為此,引入了“虛擬節(jié)點(diǎn)”的概念:即把想象在這個(gè)環(huán)上有很多“虛擬節(jié)點(diǎn)”,數(shù)據(jù)的存儲(chǔ)是沿著環(huán)的順時(shí)針方向找一個(gè)虛擬節(jié)點(diǎn),每個(gè)虛擬節(jié)點(diǎn)都會(huì)關(guān)聯(lián)到一個(gè)真實(shí)節(jié)點(diǎn),如下圖所使用:


          圖中的A1、A2、B1、B2、C1、C2、D1、D2都是虛擬節(jié)點(diǎn),機(jī)器A負(fù)載存儲(chǔ)A1、A2的數(shù)據(jù),機(jī)器B負(fù)載存儲(chǔ)B1、B2的數(shù)據(jù),機(jī)器C負(fù)載存儲(chǔ)C1、C2的數(shù)據(jù)。由于這些虛擬節(jié)點(diǎn)數(shù)量很多,均勻分布,因此不會(huì)造成“雪崩”現(xiàn)象。

           

          Java實(shí)現(xiàn):

          1. public class Shard<S> { // S類封裝了機(jī)器節(jié)點(diǎn)的信息 ,如name、password、ip、port等   
          2.   
          3.     private TreeMap<Long, S> nodes; // 虛擬節(jié)點(diǎn)   
          4.     private List<S> shards; // 真實(shí)機(jī)器節(jié)點(diǎn)   
          5.     private final int NODE_NUM = 100// 每個(gè)機(jī)器節(jié)點(diǎn)關(guān)聯(lián)的虛擬節(jié)點(diǎn)個(gè)數(shù)   
          6.   
          7.     public Shard(List<S> shards) {  
          8.         super();  
          9.         this.shards = shards;  
          10.         init();  
          11.     }  
          12.   
          13.     private void init() { // 初始化一致性hash環(huán)   
          14.         nodes = new TreeMap<Long, S>();  
          15.         for (int i = 0; i != shards.size(); ++i) { // 每個(gè)真實(shí)機(jī)器節(jié)點(diǎn)都需要關(guān)聯(lián)虛擬節(jié)點(diǎn)   
          16.             final S shardInfo = shards.get(i);  
          17.   
          18.             for (int n = 0; n < NODE_NUM; n++)  
          19.                 // 一個(gè)真實(shí)機(jī)器節(jié)點(diǎn)關(guān)聯(lián)NODE_NUM個(gè)虛擬節(jié)點(diǎn)   
          20.                 nodes.put(hash("SHARD-" + i + "-NODE-" + n), shardInfo);  
          21.   
          22.         }  
          23.     }  
          24.   
          25.     public S getShardInfo(String key) {  
          26.         SortedMap<Long, S> tail = nodes.tailMap(hash(key)); // 沿環(huán)的順時(shí)針找到一個(gè)虛擬節(jié)點(diǎn)   
          27.         if (tail.size() == 0) {  
          28.             return nodes.get(nodes.firstKey());  
          29.         }  
          30.         return tail.get(tail.firstKey()); // 返回該虛擬節(jié)點(diǎn)對(duì)應(yīng)的真實(shí)機(jī)器節(jié)點(diǎn)的信息   
          31.     }  
          32.   
          33.     /** 
          34.      *  MurMurHash算法,是非加密HASH算法,性能很高, 
          35.      *  比傳統(tǒng)的CRC32,MD5,SHA-1(這兩個(gè)算法都是加密HASH算法,復(fù)雜度本身就很高,帶來的性能上的損害也不可避免) 
          36.      *  等HASH算法要快很多,而且據(jù)說這個(gè)算法的碰撞率很低. 
          37.      *  http://murmurhash.googlepages.com/ 
          38.      */  
          39.     private Long hash(String key) {  
          40.           
          41.         ByteBuffer buf = ByteBuffer.wrap(key.getBytes());  
          42.         int seed = 0x1234ABCD;  
          43.           
          44.         ByteOrder byteOrder = buf.order();  
          45.         buf.order(ByteOrder.LITTLE_ENDIAN);  
          46.   
          47.         long m = 0xc6a4a7935bd1e995L;  
          48.         int r = 47;  
          49.   
          50.         long h = seed ^ (buf.remaining() * m);  
          51.   
          52.         long k;  
          53.         while (buf.remaining() >= 8) {  
          54.             k = buf.getLong();  
          55.   
          56.             k *= m;  
          57.             k ^= k >>> r;  
          58.             k *= m;  
          59.   
          60.             h ^= k;  
          61.             h *= m;  
          62.         }  
          63.   
          64.         if (buf.remaining() > 0) {  
          65.             ByteBuffer finish = ByteBuffer.allocate(8).order(  
          66.                     ByteOrder.LITTLE_ENDIAN);  
          67.             // for big-endian version, do this first:   
          68.             // finish.position(8-buf.remaining());   
          69.             finish.put(buf).rewind();  
          70.             h ^= finish.getLong();  
          71.             h *= m;  
          72.         }  
          73.   
          74.         h ^= h >>> r;  
          75.         h *= m;  
          76.         h ^= h >>> r;  
          77.   
          78.         buf.order(byteOrder);  
          79.         return h;  
          80.     }  
          81.   
          82. }  

          評(píng)論

          # re: 一致性哈希算法與Java實(shí)現(xiàn)   回復(fù)  更多評(píng)論   

          2014-05-22 12:25 by xdemo
          絕不可能~

          # re: 一致性哈希算法與Java實(shí)現(xiàn)   回復(fù)  更多評(píng)論   

          2014-08-12 17:37 by nodexy
          1樓高見?

          # re: 一致性哈希算法與Java實(shí)現(xiàn)   回復(fù)  更多評(píng)論   

          2015-01-15 20:37 by 沙漠綠樹
          我的qq是29561416

          我看了網(wǎng)上清一色都是拿memcached來說一致性hash,增加虛擬節(jié)點(diǎn)解決數(shù)據(jù)均衡的問題。我有個(gè)疑問:
          1.使用虛擬節(jié)點(diǎn)后,但是當(dāng)我增加物理節(jié)點(diǎn)后,環(huán)中的虛擬節(jié)點(diǎn)是否要增加,如果把他應(yīng)用在mysql上,數(shù)據(jù)遷移是否會(huì)很困難?

          2.在使用虛擬節(jié)點(diǎn)時(shí),比如5個(gè)物理節(jié)點(diǎn),每個(gè)物理節(jié)點(diǎn)虛擬出1024個(gè)虛節(jié)點(diǎn),按道理hash環(huán)有5120個(gè)節(jié)點(diǎn),但是使用kemata哈希虛擬環(huán)時(shí),有些節(jié)點(diǎn)key的哈希結(jié)果相同,導(dǎo)致hash環(huán)中少于5120個(gè)節(jié)點(diǎn)?

          # re: 一致性哈希算法與Java實(shí)現(xiàn)   回復(fù)  更多評(píng)論   

          2015-10-21 00:22 by 一個(gè)不起眼的攻城獅
          我來解決樓上的問題,解決之前賢說兩句:
          樓上太強(qiáng)勢(shì),絕對(duì)不可能,這5個(gè)字震懾到我了,而且看你的提問,只能算是懂一點(diǎn)hash一致性算法,樓主的說法是正確的,也是非常有參考價(jià)值的。
          問題答案如下:
          1. 你所說的是hash一致性的數(shù)據(jù)遷移問題,這個(gè)是hash一致性的弱點(diǎn),可以改進(jìn),但是需要自己開發(fā)相應(yīng)的程序,不是不可能完成的而是可以完成的,只是復(fù)雜點(diǎn);退一萬步,任何技術(shù)都不是萬能的,都有弊端,只是看你的業(yè)務(wù)需求最適于那種技術(shù)。
          2. 你這個(gè)可以將hash的空間編程2的32次方,2的64次方都可以,不是只能用一種,要活學(xué)活用。另外,hash出來的key值相同少有發(fā)生,這是hash的特性決定的,也就是hash沖突,這個(gè)倒是個(gè)問題,解決方法是bloomfilter算法,有興趣的可以自己去查。

          # re: 一致性哈希算法與Java實(shí)現(xiàn)   回復(fù)  更多評(píng)論   

          2016-07-25 12:04 by 三單聯(lián)咖啡色
          有一個(gè)問題,如果使用虛擬節(jié)點(diǎn),某臺(tái)機(jī)器每次宕機(jī)再恢復(fù)后都需要遷移數(shù)據(jù)。這樣是否反而更麻煩了。

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 浦城县| 买车| 三亚市| 商南县| 洪江市| 固安县| 文山县| 灵璧县| 那坡县| 康保县| 庆安县| 汨罗市| 阳山县| 同德县| 长春市| 安庆市| 准格尔旗| 常山县| 内乡县| 绩溪县| 冀州市| 西吉县| 达孜县| 赤壁市| 章丘市| 五河县| 贡嘎县| 台州市| 车险| 余干县| 冕宁县| 芦溪县| 新竹县| 雅江县| 五原县| 浏阳市| 万宁市| 宕昌县| 长海县| 鄂尔多斯市| 达拉特旗|