Jedis下的ShardedJedis(分布式)使用方法(二)

本文详细解析了ShardedJedis的工作原理,包括如何通过一致性哈希分配Redis实例上的连接,以及虚拟节点和物理连接的初始化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇中介绍了ShardedJedis的基本使用方法以及演示了一个简单的例子,在这一篇中我们来介绍了ShardedJedis的原理。

  1.ShardedJedis内部实现首先我们来看一下ShardedJedis的一个继承关系

  看完了图,那么我们一步一步跟着我们的代码调用来看,以我们最简单的ShardedJedis.get(key)方法为例:
  publicString get(String key) {
   Jedis j=getShard(key);returnj.get(key);
   }
  这边有调用一个getShard方法,参数为我们传入的key,然后返回一个普通的jedis对象,那么这个getShard是用来做什么的呢,大家可能已经猜到了,这个方法就是会根据我们传入的key做一致性哈希判断,然后返回key落到的那个redis实例上的一个redis连接,不同的key返回的redis连接可能是不同的。
  进入getShard方法,你会发现这个实现是在Sharded类中实现的(看上面的类图可以发现顶层的Sharded类),代码如下:
  publicR getShard(String key) {returnresources.get(getShardInfo(key));
   }publicS getShardInfo(byte[] key) {
   SortedMap<Long, S> tail =nodes.tailMap(algo.hash(key));if(tail.isEmpty()) {returnnodes.get(nodes.firstKey());
   }returntail.get(tail.firstKey());
   }publicS getShardInfo(String key) {returngetShardInfo(SafeEncoder.encode(getKeyTag(key)));
   }
  上面的方法是层层调用的关系,在这边不细说,我们主要看下第二个方法(getShardInfo(byte[] key))实现(上面的nodes变量是一个TreeMap类型,algo是Hashing类型,即key分片所使用的hash算法,这个在前一篇有简单说过),那么这段代码的含义我们大概成猜出来了,
  就是在一个TreeMap中取出大于等于key之后的部分视图SortMap在SortMap取得第一个键值对的值,然后返回一个S对象,然后根据这个S对象,去resources(resources = new LinkedHashMap<ShardInfo<R>, R>())中get一个R对象 
  那么这个S、R对象各自代表什么呢?看下面的代码
  publicclassSharded<R, SextendsShardInfo<R>>
  publicclassBinaryShardedJedisextendsSharded<Jedis, JedisShardInfo>implementsBinaryJedisCommands
  可以得出 S=JedisShardInfo,R=Jedis对象,即在TreeMap存储了服务器划分的虚拟节点的信息,LinkedHashMap中存储了服务器的物理连接。 
  JedisShardInfo具体信息如下:里面包含了jedis服务器的一些信息,最重要的是它的父类中有一个weight字段,作为本jedis服务器的权值。
  ok,那我们了解了实际上就是根据jedis服务器的信息去获取一个jedis的连接,返回给上层调用。
  我们可以梳理下这个逻辑:
  当我们使用ShardedJedis去查一个key时,首先它会把这个key进行一个hash算法根据这个hash值然后去treeMap中,查出这个key落在哪个实例中,并返回redis实例对应的具体信息根据这个redis的实例信息,到一个保存jedis链接和实例信息对应关系的LinkedHashMap中找到这个jedis连接最终返回jedis连接,执行对象的命令操作(到这步后实际上和单机操作一样了)那么我们的nodes服务器虚拟节点和resources服务器物理连接是什么时候初始化的呢,接下来继续看
  2.redis虚拟节点(nodes)和物理连接(resources) 初始化我们继续看Sharded的构造方法
  publicSharded(List<S>shards, Hashing algo) {this.algo =algo;
   initialize(shards);
   }publicSharded(List<S>shards, Hashing algo, Pattern tagPattern) {this.algo =algo;this.tagPattern =tagPattern;
   initialize(shards);
   }
  这边有一个initialize方法,就是用来对虚拟节点和物理连接进行初始化的,看其实现
  PRivatevoidinitialize(List<S>shards) {
   nodes=newTreeMap<Long, S>();for(inti = 0; i != shards.size(); ++i) {finalS shardInfo =shards.get(i);if(shardInfo.getName() ==null)for(intn = 0; n < 160 * shardInfo.getWeight(); n++) {
   nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" +n), shardInfo);
   }elsefor(intn = 0; n < 160 * shardInfo.getWeight(); n++) {
   nodes.put(this.algo.hash(shardInfo.getName() + "*" + shardInfo.getWeight() +n), shardInfo);
   }
   resources.put(shardInfo, shardInfo.createResource());
   }
   }
  具体细节就不说了,根据上面的,我们就知道是在这边进行了初始化,将每台服务器节点采用hash算法划分为160个虚拟节点(可以配置划分权重),保存在TreeMap中,
  然后把每台服务器节点的信息和物理连接以键值对保存LinkedHashMap中。
  然后通过一系列的查找,发现Sharded的构造方法其实是在我们jedisPool.getResource()时就完成的
  //初始化ShardedJedisPoolList<JedisShardInfo> infoList =Arrays.asList(shardInfo1, shardInfo2, shardInfo3);
   ShardedJedisPool jedisPool=newShardedJedisPool(poolConfig, infoList);
   ShardedJedis jedis= jedisPool.getResource();
  
  纳尼? 每次jedisPool.getResource()才初始化?那会不会造成很慢呢,其实不用担心,其底层是使用了commons.pool来进行连接池的一些操作,会根据我们配置的连接池参数来生成对应的连接并保存,其中的细节很复杂,博主没有继续深究,实现其实和数据库连接池是一致的。
  3.总结 
  ShardedJedis分布式具体的的实现思路:
  redis服务器节点划分:将每台服务器节点采用hash算法划分为160个虚拟节点(可以配置划分权重)
  将划分虚拟节点采用TreeMap存储
  对每个redis服务器的物理连接采用LinkedHashMap存储
  对Key or KeyTag 采用同样的hash算法,然后从TreeMap获取大于等于键hash值得节点,取最邻近节点存储;当key的hash值大于虚拟节点hash值得最大值时,存入第一个虚拟节点
  sharded采用的hash算法:md5和 MurmurHash两种;默认采用64位的MurmurHash算法;
  好了,大概的实现如上面所说的,都是图可能会有点乱,大家如果有什么问题或者发现了什么错误,please tell me!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值