Jedis中的一致性hash

本文仅供大家参考,不保证正确性,有问题请及时指出

一致性hash就不多说了,网上有很多说的很好的文章,这里说说Jedis中的Shard是如何使用一致性hash的,也为大家在实现一致性hash提供些思路。

首先是hash函数,在Jedis中有两种Hash算法可供选择,分别是MurMurHash和MD5. 按照Jedis的说法MurMur Hash更快,效果更好些。

MurmurHash.java

package redis.clients.util;

import java.nio.ByteBuffer;

import java.nio.ByteOrder;

publicclass MurmurHash implements Hashing {

publicstaticint hash(byte[] data, int seed) {

return hash(ByteBuffer.wrap(data), seed);

}

publicstaticint hash(byte[] data, int offset, int length, int seed) {

return hash(ByteBuffer.wrap(data, offset, length), seed);

}

publicstaticint hash(ByteBuffer buf, int seed) {

ByteOrder byteOrder = buf.order();

buf.order(ByteOrder.LITTLE_ENDIAN);

int m = 0x5bd1e995;

int r = 24;

int h = seed ^ buf.remaining();

int k;

while (buf.remaining() >= 4) {

k = buf.getInt();

k *= m;

k ^= k >>> r;

k *= m;

h *= m;

h ^= k;

}

if (buf.remaining() > 0) {

ByteBuffer finish = ByteBuffer.allocate(4).order(

ByteOrder.LITTLE_ENDIAN);

finish.put(buf).rewind();

h ^= finish.getInt();

h *= m;

}

h ^= h >>> 13;

h *= m;

h ^= h >>> 15;

buf.order(byteOrder);

return h;

}

publicstaticlong hash64A(byte[] data, int seed) {

return hash64A(ByteBuffer.wrap(data), seed);

}

publicstaticlong hash64A(byte[] data, int offset, int length, int seed) {

return hash64A(ByteBuffer.wrap(data, offset, length), seed);

}

publicstaticlong hash64A(ByteBuffer buf, int seed) {

ByteOrder byteOrder = buf.order();

buf.order(ByteOrder.LITTLE_ENDIAN);

long m = 0xc6a4a7935bd1e995L;

int r = 47;

long h = seed ^ (buf.remaining() * m);

long k;

while (buf.remaining() >= 8) {

k = buf.getLong();

k *= m;

k ^= k >>> r;

k *= m;

h ^= k;

h *= m;

}

if (buf.remaining() > 0) {

ByteBuffer finish = ByteBuffer.allocate(8).order(

ByteOrder.LITTLE_ENDIAN);

finish.put(buf).rewind();

h ^= finish.getLong();

h *= m;

}

h ^= h >>> r;

h *= m;

h ^= h >>> r;

buf.order(byteOrder);

return h;

}

publiclong hash(byte[] key) {

return hash64A(key, 0x1234ABCD);

}

publiclong hash(String key) {

return hash(SafeEncoder.encode(key));

}

}

而MD5从java提供的类中即可获得,但是需要将MD5值转换为long类型的常量。

publicinterface Hashing {

publicstaticfinal Hashing MURMUR_HASH = new MurmurHash();

public ThreadLocal<MessageDigest> md5Holder = new ThreadLocal<MessageDigest>();

publicstaticfinal Hashing MD5 = new Hashing() {

publiclong hash(String key) {

return hash(SafeEncoder.encode(key));

}

publiclong hash(byte[] key) {

try {

if (md5Holder.get() == null) {

md5Holder.set(MessageDigest.getInstance("MD5"));

}

} catch (NoSuchAlgorithmException e) {

thrownew IllegalStateException("++++ no md5 algorythm found");

}

MessageDigest md5 = md5Holder.get();

md5.reset();

md5.update(key);

byte[] bKey = md5.digest();

long res = ((long) (bKey[3] & 0xFF) << 24)

| ((long) (bKey[2] & 0xFF) << 16)

| ((long) (bKey[1] & 0xFF) << 8) | (long) (bKey[0] & 0xFF);

return res;

}

};

}

MD5中使用了ThreadLocal,这个就不多说了,网上也有很多相关的blog。或者查看我《如何写性能测试程序》那篇博客。

下面主要说说,Jedis中的一致性Hash部分。

Shard中定义了如下属性:

private TreeMap<Long, S> nodes;

privatefinal Hashing algo;

privatefinal Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>();

nodes属性类型为TreeMap<Long,S>,表示主机映射。每个主机被Hash后,对应多个Long值,而nodes维护了这些Long值对应的主机。

algo是hash算法,是MurMur或者MD5.

resources 完成主机信息和实际链接(Jedis)间的映射。

初始化主机hash如下:

privatevoid initialize(List<S> shards) {

nodes = new TreeMap<Long, S>();

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++) {

nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo);

}

else

for (int n = 0; n < 160 * shardInfo.getWeight(); n++) {

nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() + n), shardInfo);

}

resources.put(shardInfo, shardInfo.createResource());

}

}

在for循环中,遍历主机列表(shards.get(i)),之后对每个主机按照单权重160的比例计算shard值,将shard值和主机信息(shardInfo)放到nodes中,将主机信息(shardInfo)和其对应的链接资源(Jedis)映射放入到resources中。

Weight是权重,用于调节单个主机被映射值个数,如果weight为1,那么当前主机将被映射为160个值,weight为2,当前主机将被映射为320个值,因此weight为2的节点被访问到的概率就会高一些。

获取某个key对应的主机链接:

public S getShardInfo(byte[] key) {

SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));

if (tail.isEmpty()) {

returnnodes.get(nodes.firstKey());

}

return tail.get(tail.firstKey());

}

在获取key时,先根据key做hash(algo.hash(key)),然后获取node中值大于algo.hash(key)的子树,子树的头结点即为整个子树的最小值,因此选中这个节点做为key的映射节点,取出key值对应的value(hostInfo),之后根据hostInfo,通过resources,获取最终的链接。

public R getShard(byte[] key) {

returnresources.get(getShardInfo(key));

}

当然,一致性hash在存储时,一个key可以对应多个节点,一般这多个节点都是在nodes(MapTree)中连续的,因此,也可以改进这个算法,以便实现多节点存储同一个key的方式。

参考:jedis-2.1.0.jar

Jedis中的一致性hash的更多相关文章

  1. jedis中的一致性hash算法

    [http://my.oschina.net/u/866190/blog/192286] jredis是redis的java客户端,通过sharde实现负载路由,一直很好奇jredis的sharde如 ...

  2. 分布式缓存技术memcached学习(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...

  3. Ceph剖析:数据分布之CRUSH算法与一致性Hash

    作者:吴香伟 发表于 2014/09/05 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 数据分布是分布式存储系统的一个重要部分,数据分布算法至少要考虑以下三个 ...

  4. 【转载】一致性hash算法释义

    http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Karge ...

  5. python -- 一致性Hash

    python有一个python模块--hash_ring,即python中的一致性hash,使用起来也挺简单. 可以参考下官方例子:https://pypi.python.org/pypi/hash_ ...

  6. 一致性Hash算法及使用场景

    一.问题产生背景      在使用分布式对数据进行存储时,经常会碰到需要新增节点来满足业务快速增长的需求.然而在新增节点时,如果处理不善会导致所有的数据重新分片,这对于某些系统来说可能是灾难性的. 那 ...

  7. Ceph之数据分布:CRUSH算法与一致性Hash

    转自于:http://www.cnblogs.com/shanno/p/3958298.html?utm_source=tuicool 数据分布是分布式存储系统的一个重要部分,数据分布算法至少要考虑以 ...

  8. 分布式缓存技术memcached学习系列(四)—— 一致性hash算法原理

    分布式一致性hash算法简介 当你看到"分布式一致性hash算法"这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前, ...

  9. [转载] 一致性hash算法释义

    转载自http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Ka ...

随机推荐

  1. Servlet基础知识(四)——Servlet过滤器Filter

    一.什么是过滤器: 政府大楼的安检保安,它既能对进入政府大楼的人员进行检查,只允许检查符合要求的进入:同时他也负责对出大楼的人进行检查,看他带出的东西是否符合要求. 同样的,Servlet中的过滤器既 ...

  2. 1501 二叉树最大宽度和高度 (BFS+树的遍历)

    题目:http://www.wikioi.com/problem/1501/ 给你一颗二叉树,求该数的宽和高, 首先求出树的高,直接进行二叉树遍历,能够得到二叉树的高 然后是得到宽,本人采用的是一层一 ...

  3. Visual Studio 常用快捷键总结

    删除或剪切一行:  Ctrl + X 或者 Shift+Delete格式化整个文档:  Ctrl + K + D 或者 Ctrl+E+D智能感知: Ctrl + J 或者 Alt+→折叠所有方法: C ...

  4. The error indicates that IIS is in 32 bit mode, while this application is a 64 b it application and thus not compatible.

    I was trying to install a new WSS v3 Sharepoint on a 64 bit Windows 2003 server today but the instal ...

  5. 转: angular编码风格指南

    After reading Google's AngularJS guidelines, I felt they were a little too incomplete and also guide ...

  6. WA(Write Amplification)写入放大

    WA是闪存及SSD相关的一个极为重要的属性.由于闪存必须先擦除才能再写入的特性,在执行这些操作时,数据都会被移动超过1次.这些重复的操作不单会增加写入的数据量,还会减少闪存的寿命,更吃光闪存的可用带宽 ...

  7. 命令行方式运行yii2程序

    测试环境,yii 2.0.3版本 web访问方式,控制器放在controllers目录下 ,浏览器访问如下地址 http://127.0.0.1/index.php?r=[controller-nam ...

  8. telnet登陆路由器。。。

    登陆路由有两种方式.一种是console,还有一种是使用telnet,由于我电脑是win7,不支持console.也懒得装软件,就使用telnent为例. 一.开启telnetclient 对于XP. ...

  9. 替换Avada主题的Google字体

    刚玩WP的时候图省事,在themeforest买了排行第一的主题Avada,虽然强大,但对我目前的Blog应用而言实在太'重'了.而且老外的主题很多方面不接地气,比如谷歌字体.本文指导各位如何在Ava ...

  10. FMDB 直接将查询结果转化为字典

    今天学习FMDB框架,发现非常好用的一点,就是就以把查询结果直接转化为字典 NSString *querySql = @"select * from stuInfo"; NSMut ...