Jedis中的一致性hash
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的更多相关文章
- jedis中的一致性hash算法
[http://my.oschina.net/u/866190/blog/192286] jredis是redis的java客户端,通过sharde实现负载路由,一直很好奇jredis的sharde如 ...
- 分布式缓存技术memcached学习(四)—— 一致性hash算法原理
分布式一致性hash算法简介 当你看到“分布式一致性hash算法”这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前,我们先来了解一下这几 ...
- Ceph剖析:数据分布之CRUSH算法与一致性Hash
作者:吴香伟 发表于 2014/09/05 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 数据分布是分布式存储系统的一个重要部分,数据分布算法至少要考虑以下三个 ...
- 【转载】一致性hash算法释义
http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Karge ...
- python -- 一致性Hash
python有一个python模块--hash_ring,即python中的一致性hash,使用起来也挺简单. 可以参考下官方例子:https://pypi.python.org/pypi/hash_ ...
- 一致性Hash算法及使用场景
一.问题产生背景 在使用分布式对数据进行存储时,经常会碰到需要新增节点来满足业务快速增长的需求.然而在新增节点时,如果处理不善会导致所有的数据重新分片,这对于某些系统来说可能是灾难性的. 那 ...
- Ceph之数据分布:CRUSH算法与一致性Hash
转自于:http://www.cnblogs.com/shanno/p/3958298.html?utm_source=tuicool 数据分布是分布式存储系统的一个重要部分,数据分布算法至少要考虑以 ...
- 分布式缓存技术memcached学习系列(四)—— 一致性hash算法原理
分布式一致性hash算法简介 当你看到"分布式一致性hash算法"这个词时,第一时间可能会问,什么是分布式,什么是一致性,hash又是什么.在分析分布式一致性hash算法原理之前, ...
- [转载] 一致性hash算法释义
转载自http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Ka ...
随机推荐
- Labview学习之程序Web发布
Labview学习之程序Web发布 1. LabVIEW Web服务器 在LabVIEW开发环境中,自身带了一个已连接好的Web服务器.LabVIEW Web服务器除了与其他Web服务器一样能 ...
- .Net 类型、对象、线程栈、托管堆运行时的相互关系
JIT(just in time)编译器 接下来的会讲到方法的调用,这里先讲下JIT编译器.以CLR书中的代码为例(手打...).以Main方法为例: static void Main(){ Cons ...
- Web API (一)
页面内容 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> ...
- break的使用例一
/* Name:break的使用例一 Copyright: By.不懂网络 Author: Yangbin Date:2014年2月21日 02:28:24 Description:本程序代码无如何含 ...
- Linux内核和驱动编译常见问题
1.获取内核源码应为 git clone depth=1 不然编译时会报错 2.有些RTL188CUS网卡与wpa_supplicant兼容有问题 更新wpasupplicant或许能解决 这里用i ...
- nyist 220 推桌子
题目链接:推桌子 题目意思:给你一些操作,将S出的桌子推到L出,但是这个过道有时会被占用,推一次是10min,不影响的操作可以同时开始,并且只记一次. 思路:贪心,首先按照S从小到大排序,决策:从第一 ...
- Android中自定义属性的使用
做Android布局是件很享受的事,这得益于他良好的xml方式.使用xml可以快速有效的为软件定义界面.可是有时候我们总感觉官方定义的一些基本组件不够用,自定义组件就不可避免了.那么如何才能做到像官方 ...
- css兼容问题与实践归纳总结
css兼容问题与实践归纳总结 一.IE6/7 原生块元素与display:inline-block; <div style="display:inline-block;"&g ...
- [VC6 console]调用API获取手机归属地
为了完成作业,就偷个懒糊了个获取手机归属地的程序,.我原本写的是MFC版本的,但是由于MFC的代码不是很通用,加上我没有学MFC的时候看别人MFC代码只能干瞪眼,看不懂,所以便改成控制台版本的了.但这 ...
- jquery submit()不能提交表单的解决方法
<form id="form" method="get"> <input type="text" name="q ...