Jedis 默认sharding实现原理分析
? ? ? ? ??}? ? ? ? ? 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());? ? ? }? }
?其中我们看出JedisShardedPool通过每一个JedisShardedInfo配置的连接的name属性类分箱。具体做法是,每个Redis连接根据其名字+权重+计数号等信息
进行160次哈希计算作为160个?“沙箱孔”key(一个Long值)都对应一个redis连接信息JedisShardedInfo实例。假设有2个redis连接信息如下:
- ?List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();?shards.add(new JedisShardInfo("localhost", 6379, "master1"));?shards.add(new JedisShardInfo("localhost", 6380, "master2"));
?
?则:
?名字为master1,端口为6379的连接在pool中对应根据名字master1+权重+计数号生成的160个沙箱孔。
?名字为master2,端口为6380的连接在pool中对应根据名字master2+权重+计数号生成的160个沙箱孔.
?注:为什么会生成160个呢?个人认为这个数字是考虑到redis key 值能在各连接中均匀分布而为之。
?
?这样当我们通过key对redis进行操作时,会用同样的hash算法对该key进行hash操作,然后在所有的沙箱孔(连接的keys)中找到那个力该key值最接近的那个孔(连接在pool中的key值),
获取?redis连接,进行相应的操作。
?
?举个例子,进行如下操作:
- ?ShardedJedis jedis = shardedJedisPool.getResource();?jedis.set("key1", "value1");
?
?看ShardedJedis.java源码如下:
- ? public String set(String key, String value) {Jedis j = getShard(key);return j.set(key, value);? }
??
?首先要调用getShard(key)方法获取当前要操作的key对应的连接实例,继续看getShard()方法源码:
- ? public R getShard(String key) {? ??return resources.get(getShardInfo(key));? }
??
?为从resources中获取连接实例,需要知道该连接实例在reeMap<Long, S> nodes 中对应哪个沙箱孔?
- ?public S getShardInfo(byte[] key) {? ??SortedMap<Long, S> tail =nodes.tailMap(algo.hash(key));? ??if (tail.isEmpty()) {? ? ? ??return nodes.get(nodes.firstKey());? ??}? ??return tail.get(tail.firstKey());?}
?
- ?public S getShardInfo(String key) {? ??return getShardInfo(SafeEncoder.encode(getKeyTag(key)));?}
?
?重点在getShardInfo(byte[] key)的实现,根据key的hash值,找到nodes中比它大的key值(沙箱孔)。如果没有,则拿第一个孔,如果存在则取比它大的第一个孔。
?这样对key的save/query类操作都能映射到沙箱中的同一个孔,获取相同的连接,保证数据的一致和完整性。
?
?以上是Jedis沙盒机制的默认实现,但在实际应用中我们不一定要采用这样的机制。