redis cluster 集群模式下执行批量处理命令会报错,具体原因如下:不同的 key 计算出的 slot 槽不一样,不同槽可能对应到不同节点,也就是说批量操作实际很可能被均分到不同的 redis 实例上,对于这种情况,redis cluster 没法处理,直接报错
一般有两种方法解决该问题:
1、别用批量处理命令了,老老实实一条一条执行,对于某些不追求效率的场景确实可以这样干
2、虽然 cluster 模式下没法批量处理,但我们知道 slot 槽怎么计算,也知道对应槽属于哪台实例,此时就可以在客户端手动计算,通过 pipeline 连接位于同一实例下的 key,批量处理
下面我通过简单代码模拟整个过程:
public static Map<String, String> mGet(JedisCluster jedisCluster, String... keys) {
Map<String, String> result = new HashMap<>();
if (keys == null || keys.length == 0) {
return result;
}
if (keys.length == 1) {
result.put(keys[0], jedisCluster.get(keys[0]));
return result;
}
// 需要引入 Mybatis 包,网上查的,通过这种方式能获取到 slot 和 jedisPool 的对应关系
MetaObject metaObject = SystemMetaObject.forObject(jedisCluster);
JedisClusterInfoCache cache = (JedisClusterInfoCache) metaObject.getValue("connectionHandler.cache");
// 用来记录各个实例包含哪些 key
Map<JedisPool, List<String>> map = new HashMap<>();
// 遍历所有 key
for (String s : keys) {
int slot = JedisClusterCRC16.getSlot(s);
JedisPool temp = cache.getSlotPool(slot);
if (map.containsKey(temp)) {
map.get(temp).add(s);
} else {
List<String> list = new ArrayList<>();
list.add(s);
map.put(temp, list);
}
}
// 遍历所有存在 key 的 redis 实例,通过 pipeline 批量执行
for (Map.Entry<JedisPool, List<String>> entry : map.entrySet()) {
JedisPool pool = entry.getKey();
List<String> list = entry.getValue();
Pipeline pipeline = pool.getResource().pipelined();
for (String s : list) {
pipeline.get(s);
}
List<Object> objectList = pipeline.syncAndReturnAll();
pipeline.close();
for (int i = 0; i < objectList.size(); ++i) {
result.put(list.get(i), objectList.get(i) == null ? null : objectList.get(i).toString());
}
}
return result;
}