在Redis集群中,会有不少个分片,若是此时利用Jedis来操做此Redis集群,那么他会把数据路由到不到的分片上。并且若是动态的往集群中增长分片,也不会影响Jedis的功能。到底是怎么作到的呢?css
因为最近公司要集中迁移redis集群,也就是把旧集群的数据迁移到Redis Cluster中,就须要咱们本身来整理数据。恰好我这里有个库存热点数据,咱们叫作A吧,这个A在Redis集群中,每一个分片上都有数据。好比,Redis集群有4个片,而我总库存量为1000,那么会在4个片上放上Key A,每一个A中库存量为250.html
新的Redis cluster,为了防止热点库存问题,也就申请了4个cluster,每一个cluster至关于以前的一个分片。数据在写入的时候,每一个cluster会写一个key A,库存量为250.node
如今问题来了,有一些Key,好比说B,在原来的Redis集群中,是利用Jedis路由来导向某一片写入的,假设这里写入的是第3片。若是迁移到新的4个Redis cluster中,势必须要写到第三个Redis Cluster中。由于数据迁移,确定是第一片redis数据迁移到第一个Cluster,第二片redis数据迁移到第二个Cluster,以此类推。redis
因此这里须要咱们来实现和Jedis同样的路由算法,按照Jedis提供的类,咱们实现以下:算法
首先,定义好ShardNode,继承自Jedis的ShardInfo:app
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类的源码):dom
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,因此咱们的路由算法才可以按照配置的分片顺序进行路由。 ide
而后进行实现便可:测试
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路由算法和咱们所写的路由算法是一致的。this
@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。