转载:https://www.iteblog.com/archives/2499.html
http://www.zsythink.net/archives/1182
上面两篇文章对一致性哈希算法的原理讲的非常好,但对虚拟节点方面讲的粒度比较粗,下面我结合ShardedJedis的源码,简单说下自己的理解。
当我们的项目启动好之后,根据业务的需要,去操作redis服务器时,需要通过这个命令
ShardedJedis jedis = pool.getResource();在线程池里获取连接;在执行这行命令的过程中,会初始化各个redis实体节点对应的虚拟节点,代码如下:
//List<S> shards:各个redis实体节点
//nodes:存放各个redis实体节点对应的160个虚拟节点;nodes的类型是TreeMap,而TreeMap的底层实现是红黑树,红黑树其实是一棵自平衡的排序二叉树,其执行查找、插入、删除等操作的时间复杂度为O(logn)。看到这里的小伙伴,应该明白为什么使用TreeMap来存储redis实体节点的虚拟节点了吧。
private void initialize(List<S> shards) { nodes = new TreeMap<Long, S>(); //shards.size():redis实体节点的个数 for (int i = 0; i != shards.size(); ++i) { final S shardInfo = shards.get(i); if (shardInfo.getName() == null) //每个redis实体节点会产生160个虚拟节点,把这160个虚拟节点存入ThreeMap中,这160个虚拟节点的value都是对应自己的redis实体节点,每个虚拟节点的key在TreeMap中都是唯一的--i的值决定了一个虚拟节点属于哪个redis实体节点,n的值决定了属于哪个redis实体节点的哪个虚拟节点 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()); } }
有了上面的初始化之后,在操作redis服务器时,首先根据key计算出对应的哈希值,然后再根据这个哈希值在TreeMap中找到离它最近的虚拟节点,再通过虚拟节点找到对应的redis实体节点,这样,就可以操作对应的redis服务器了。