從jredis中學習一致性hash算法

jredis是redis的java客戶端,經過sharde實現負載路由,一直很好奇jredis的sharde如何實現,翻開jredis源碼研究了一番,所謂sharde其實就是一致性hash算法。其實,經過其源碼能夠看出一致性hash算法實現仍是比較簡單的。主要實現類是redis.clients.util.Sharded<R, S>,關鍵的地方添加了註釋: java


public class Sharded<R, S extends ShardInfo<R>> { //S類封裝了redis節點的信息 ,如name、權重

    public static final int DEFAULT_WEIGHT = 1;//默認權重爲1
    private TreeMap<Long, S> nodes;//存放虛擬節點
    private final Hashing algo;//hash算法
    
    ......

    public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
        this.algo = algo;
        this.tagPattern = tagPattern;
        initialize(shards);
    }

    private void initialize(List<S> shards) {
        nodes = new TreeMap<Long, S>();//基於紅黑樹實現排序map, 是根據key排序的 ,注意這裏key放的是long類型,最多放2^32個

        for (int i = 0; i != shards.size(); ++i) {
            final S shardInfo = shards.get(i);
            if (shardInfo.getName() == null)
            	for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
            		//一個真實redis節點關聯多個虛擬節點  , 經過計算虛擬節點hash值,可很好平衡把它分散到2^32個整數上
            		nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);
            	}
            else
            	for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {
            		//一個真實redis節點關聯多個虛擬節點  , 經過計算虛擬節點hash值,可很好平衡把它分散到2^32個整數上
            		nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo);
            	}
            resources.put(shardInfo, shardInfo.createResource());
        }
    }
   

    /**
     * 計算key的hash值查找實際實際節點S
     * @param key
     * @return
     */
    public S getShardInfo(byte[] key) {
        SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));//取出比較key的hash大的
        if (tail.isEmpty()) {//取出虛擬節點爲空,直接取第一個
            return nodes.get(nodes.firstKey());
        }
        return tail.get(tail.firstKey());//取出虛擬節點第一個
    }

    ......
}


整個算法可總結爲:首先生成一個長度爲2^32個整數環,經過計算虛擬節點hash值映射到整數環上,間接也把實際節點也放到這個環上(由於虛擬節點會關聯上一個實際節點)。而後根據須要緩存數據的key的hash值在整數環上查找,環順時針找到距離這個key的hash值最近虛擬節點,這樣就完成了根據key到實際節點之間的路由了。 node

一致性hash核心是思想是增長虛擬節點這一層來解決實際節點變更而不破壞總體的一致性。這種增長層的概念來解決問題對於咱們來講一點都不陌生,如軟件開發中分層設計,操做系統層解決了應用層和硬件的協調工做,java虛擬機解決了跨平臺。 redis

還有一個問題值得關注是一個實際節點虛擬多少個節點纔是合適呢?認真看過上述代碼同窗會注意160這個值,這個其實是經驗值,太多會影響性能,太少又會影響不均衡。經過調整weight值,可實現實際節點權重,這個很好理解,虛擬出節點越多,落到這個節點機率越高。 算法

參考資料 緩存

http://blog.csdn.net/sparkliang/article/details/5279393 性能

http://my.oschina.net/u/90679/blog/188750 this

相關文章
相關標籤/搜索