posts - 110, comments - 101, trackbacks - 0, articles - 7
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          一致性哈希算法與Java實現

          Posted on 2012-10-10 11:32 云云 閱讀(48852) 評論(5)  編輯  收藏
            一致性哈希算法是分布式系統中常用的算法。比如,一個分布式的存儲系統,要將數據存儲到具體的節點上,如果采用普通的hash方法,將數據映射到具體的節點上,如key%N,key是數據的key,N是機器節點數,如果有一個機器加入或退出這個集群,則所有的數據映射都無效了,如果是持久化存儲則要做數據遷移,如果是分布式緩存,則其他緩存就失效了。

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


           

          把數據用hash函數(如MD5),映射到一個很大的空間里,如圖所示。數據的存儲時,先得到一個hash值,對應到這個環中的每個位置,如k1對應到了圖中所示的位置,然后沿順時針找到一個機器節點B,將k1存儲到B這個節點中。

          如果B節點宕機了,則B上的數據就會落到C節點上,如下圖所示:


           

          這樣,只會影響C節點,對其他的節點A,D的數據不會造成影響。然而,這又會造成一個“雪崩”的情況,即C節點由于承擔了B節點的數據,所以C節點的負載會變高,C節點很容易也宕機,這樣依次下去,這樣造成整個集群都掛了。

                 為此,引入了“虛擬節點”的概念:即把想象在這個環上有很多“虛擬節點”,數據的存儲是沿著環的順時針方向找一個虛擬節點,每個虛擬節點都會關聯到一個真實節點,如下圖所使用:


          圖中的A1、A2、B1、B2、C1、C2、D1、D2都是虛擬節點,機器A負載存儲A1、A2的數據,機器B負載存儲B1、B2的數據,機器C負載存儲C1、C2的數據。由于這些虛擬節點數量很多,均勻分布,因此不會造成“雪崩”現象。

           

          Java實現:

          1. public class Shard<S> { // S類封裝了機器節點的信息 ,如name、password、ip、port等   
          2.   
          3.     private TreeMap<Long, S> nodes; // 虛擬節點   
          4.     private List<S> shards; // 真實機器節點   
          5.     private final int NODE_NUM = 100// 每個機器節點關聯的虛擬節點個數   
          6.   
          7.     public Shard(List<S> shards) {  
          8.         super();  
          9.         this.shards = shards;  
          10.         init();  
          11.     }  
          12.   
          13.     private void init() { // 初始化一致性hash環   
          14.         nodes = new TreeMap<Long, S>();  
          15.         for (int i = 0; i != shards.size(); ++i) { // 每個真實機器節點都需要關聯虛擬節點   
          16.             final S shardInfo = shards.get(i);  
          17.   
          18.             for (int n = 0; n < NODE_NUM; n++)  
          19.                 // 一個真實機器節點關聯NODE_NUM個虛擬節點   
          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)); // 沿環的順時針找到一個虛擬節點   
          27.         if (tail.size() == 0) {  
          28.             return nodes.get(nodes.firstKey());  
          29.         }  
          30.         return tail.get(tail.firstKey()); // 返回該虛擬節點對應的真實機器節點的信息   
          31.     }  
          32.   
          33.     /** 
          34.      *  MurMurHash算法,是非加密HASH算法,性能很高, 
          35.      *  比傳統的CRC32,MD5,SHA-1(這兩個算法都是加密HASH算法,復雜度本身就很高,帶來的性能上的損害也不可避免) 
          36.      *  等HASH算法要快很多,而且據說這個算法的碰撞率很低. 
          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. }  

          評論

          # re: 一致性哈希算法與Java實現   回復  更多評論   

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

          # re: 一致性哈希算法與Java實現   回復  更多評論   

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

          # re: 一致性哈希算法與Java實現   回復  更多評論   

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

          我看了網上清一色都是拿memcached來說一致性hash,增加虛擬節點解決數據均衡的問題。我有個疑問:
          1.使用虛擬節點后,但是當我增加物理節點后,環中的虛擬節點是否要增加,如果把他應用在mysql上,數據遷移是否會很困難?

          2.在使用虛擬節點時,比如5個物理節點,每個物理節點虛擬出1024個虛節點,按道理hash環有5120個節點,但是使用kemata哈希虛擬環時,有些節點key的哈希結果相同,導致hash環中少于5120個節點?

          # re: 一致性哈希算法與Java實現   回復  更多評論   

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

          # re: 一致性哈希算法與Java實現   回復  更多評論   

          2016-07-25 12:04 by 三單聯咖啡色
          有一個問題,如果使用虛擬節點,某臺機器每次宕機再恢復后都需要遷移數據。這樣是否反而更麻煩了。

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 深水埗区| 资阳市| 新余市| 武宣县| 西乌| 达日县| 苍溪县| 鸡西市| 佛学| 枣阳市| 深水埗区| 阿图什市| 略阳县| 崇明县| 偃师市| 信宜市| 大英县| 华宁县| 林周县| 肥东县| 广丰县| 夏邑县| 方城县| 安庆市| 镇沅| 策勒县| 惠来县| 裕民县| 曲水县| 华容县| 北安市| 南木林县| 台前县| 河北区| 双桥区| 桐柏县| 甘德县| 顺平县| 安宁市| 大洼县| 武平县|