Jedis路由key的算法剥离

作者: java高并发 | 来源:发表于2019-03-19 16:27 被阅读19次

    在Redis集群中,会有很多个分片,如果此时利用Jedis来操作此Redis集群,那么他会把数据路由到不到的分片上。而且如果动态的往集群中增加分片,也不会影响Jedis的功能。究竟是怎么做到的呢?

    由于最近公司要集中迁移redis集群,也就是把旧集群的数据迁移到Redis Cluster中,就需要我们自己来整理数据。刚好我这里有个库存热点数据,我们叫做A吧,这个A在Redis集群中,每个分片上都有数据。比如,Redis集群有4个片,而我总库存量为1000,那么会在4个片上放上Key A,每个A中库存量为250.

    新的Redis cluster,为了防止热点库存问题,也就申请了4个cluster,每个cluster相当于之前的一个分片。数据在写入的时候,每个cluster会写一个key A,库存量为250.

    现在问题来了,有一些Key,比如说B,在原来的Redis集群中,是利用Jedis路由来导向某一片写入的,假设这里写入的是第3片。如果迁移到新的4个Redis cluster中,势必需要写到第三个Redis Cluster中。因为数据迁移,肯定是第一片redis数据迁移到第一个Cluster,第二片redis数据迁移到第二个Cluster,以此类推。

    所以这里需要我们来实现和Jedis一样的路由算法,按照Jedis提供的类,我们实现如下:

    首先,定义好ShardNode,继承自Jedis的ShardInfo:

    public class ShardNode implements ShardInfo {
    
        public ShardNode(String index, Cluster cluster) {
            this.index = index;
            this.cluster = cluster;
        }
    
        private String index;
    
        private Cluster cluster;
    
        @Override
        public int getWeight() {
            return 1;
        }
    
        @Override
        public String getName() {
            return null;
        }
    
        public Cluster getCluster() {
            return cluster;
        }
    
        public void setCluster(Cluster cluster) {
            this.cluster = cluster;
        }
    
        public String getIndex() {
            return index;
        }
    
        public void setIndex(String index) {
            this.index = index;
        }
    }
    

    注意,getName方法里面一定要return null, 这样就会根据配置的分片的数量先后顺序来运算哈希key。具体源码如下(翻看KetamaHashing类的源码):

    
       protected void initialize(List<T> shards) {
           Charset charset = Charset.forName("utf-8");
           String key = null;
    
           for(int i = 0; i < shards.size(); ++i) {
               T shardInfo = (ShardInfo)shards.get(i);
               if (shardInfo == null) {
                   throw new IllegalArgumentException("shard element #" + i + " is null.");
               }
    
               AtomicReference<T> wrapper = new AtomicReference(shardInfo);
               this.originals.add(wrapper);
    
               for(int n = 0; n < 160 * shardInfo.getWeight(); ++n) {
                   if (shardInfo.getName() == null) {
                       key = "SHARD-" + i + "-NODE-" + n;
                   } else {
                       key = shardInfo.getName() + "*" + shardInfo.getWeight() + n;
                   }
    
                   this.nodes.put(this.algo.hash(key.getBytes(charset)), wrapper);
               }
           }
    
       }
    

    注意我标黄颜色部分,正式因为shardInfo.getName为null,所以我们的路由算法才能够按照配置的分片顺序进行路由。
    然后进行实现即可:

     public Cluster getCluster(String key) {
            KetamaHashing  ketamaHashing = new KetamaHashing(shardNodes, new MurmurHash());
            ShardNode shard = (ShardNode) ketamaHashing.getShardInfo(key.getBytes());
            return shard.getCluster();
        }
    

    这样我们就可以通过key来获取新的Redis Cluster实例了。通过测试用例结果,我们也可以看出Jedis路由算法和我们所写的路由算法是一致的。

     @Test
        public void testJedisHash() {
    
            Map<String, String> map = new HashMap<>();
            map.put("192.168.155.84:6379", "0");
            map.put("192.168.155.84:6390", "1");
            map.put("192.168.155.85:6379", "2");
            map.put("192.168.155.85:6390", "3");
    
            String key;
            List<String> list = new ArrayList<>();
    
    
            for (int i = 0; i < 1000; i++) {
    
                key = UUID.randomUUID().toString();
    
                ShardNode cluster = storeJimdbs.getShardNode(key);
    
                ShardInfo redisShard = jrc.getShardInfo(key);
                String shardKey = redisShard.toString().split("/")[0];
    
                if (cluster.getIndex().equals(map.get(shardKey))) {
                    list.add("OK");
                } else {
                    System.out.println("NNNNNNNotMatch!!!!!!!");
                }
            }
    
            System.out.println(list.size());
        }
    

    最后运算出来的结果是1000,也就是说1000个随机key,利用Jedis路由算法操作,利用我们写的路由算法操作,其实是打到相同的分片序号上的。

    也就是0号分片对应0号cluster。

    欢迎工作一到五年的Java工程师朋友们加入Java高并发: 957734884,群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!


    相关文章

      网友评论

        本文标题:Jedis路由key的算法剥离

        本文链接:https://www.haomeiwen.com/subject/euknmqtx.html