zoukankan      html  css  js  c++  java
  • 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采用的 hash算法是 MurmurHash,这个hash算法是一种非加密型算法,其会比MD5,SHA1,SHA256这些加密算法快很多倍,目前Redis,Memcached,Cassandra,HBase,Lucene都是使用这个hash算法。

  • 相关阅读:
    Zookeeper系列(二)特征及应用场景
    Scala学习笔记(三)类层级和特质
    zookeeper系列(一)安装
    Scala学习笔记(二)表达式和函数
    Spring笔记(四)SpingAOP
    Spring笔记(三)AOP前篇之动态代理
    Scala学习笔记(一)数据类型
    Linux内核系列设备模型(一) Kobject与Kset
    Spring笔记(二)Core层
    Linux内核系列之Block块层(一)
  • 原文地址:https://www.cnblogs.com/lianshan/p/11644925.html
Copyright © 2011-2022 走看看