jedis的ShardedJedisPool链接池的扩容问题

Posted lianshan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jedis的ShardedJedisPool链接池的扩容问题相关的知识,希望对你有一定的参考价值。

回顾上一篇文章jedis连接池的构建。

我们来分析ShardedJedisPool的基于客户端分片所可能带来的问题:扩容

ShardedJedisPool的节点扩容 。ShardedJedisPool采用的 是客户端分片模式 ,我们来看一下Sharded的初始化代码,获取节点信息后,其节点按照权重*160个的虚拟节点,将创建的节点放在TreeMap中,value为虚拟节点对应的 正式分片信息。

 private void 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());
    }
  }
 

至于为什么创建160个虚拟节点与一致性hash一致,单一的节点很可能导致数据的分布不均。

 

其后只要遵循同样的 规则,set和get,即可满足相同的 key到相同的 节点上,其代码如下:

其后我们在每次之心jedis操作是先通过可以获取其对应的连接,其代码如下,其依赖于treemapd.tailMap()实现:

public R getShard(String key) {
    return resources.get(getShardInfo(key));
  }

  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)));
  }

回到我们的问题,扩容

虚拟节点的创建将数据均匀的分布到各个节点,但是随着节点的扩容,伴随的问题节点数据的迁移与拷贝,但是什么节点此hash并不是一致性hash,也不是集群模式可以很容易的分配槽点,我们也很难确定什么样的数据需要拷贝到什么新的节点,所以这就给节点扩容带来了困难,扩容必然存在redis数据的丢失,需要有相应的 兼容策略,如数据缺失的兜底策略。

那么我们有什么办法来避免这种情况么 ?

最简单的 就是 prehash,即提前创建相对多的分片,可以将多个集中在单台服务器上,这虽然会带来一定资源的浪费,但是在数据膨胀需要迁移时,我们只需将相应的节点迁移出去,就避免了扩容的 问题。

 

以上是关于jedis的ShardedJedisPool链接池的扩容问题的主要内容,如果未能解决你的问题,请参考以下文章

Jedis/JedisPool和Redis数据类型与特性

Redis客户端之Spring整合Jedis

ShardedJedis的使用

Java中使用Redis

redis简单配置

Jedis连接池配置参数