JedisCluster反复重定向的根本原因是其对MOVED和ASK响应的处理不一致:MOVED触发slot映射更新,ASK却不更新,导致持续重试旧节点直至超限;mget跨槽必失败,因Redis Cluster强制多key操作须同槽;安全的跨槽批量操作需手动按slot分组并使用Pipeline。

为什么 JedisCluster 会反复重定向
根本原因不是客户端“写错了”,而是它在两种重定向场景下行为不一致:MOVED(槽已归属新节点)和 ASK(槽正在迁移中)。JedisCluster 内部缓存了 slot → node 映射,但这个映射只在初始化或收到 MOVED 响应时更新;而 ASK 响应不会触发刷新,导致后续请求仍发往旧节点,反复被 ASK → 重试 → 再 ASK,直到超过 maxRedirections(默认 5 或 16)。
mget 跨槽失败却不出错的假象
看似 mget 只是批量读,但它要求所有 key 必须落在同一 slot —— 否则直接抛 JedisClusterMaxRedirectionsException。这不是 bug,是 Redis Cluster 协议强制限制:多 key 操作必须原子、同槽。
- 所有 key 都在同一个哈希槽 → 成功返回值列表
- 任意 key 分属不同槽 → 触发重定向链,极易超限
- 哪怕只差一个 key 跨槽,整个
mget就会失败,而不是部分成功
用 Pipeline 绕过重定向陷阱
真正可控的跨槽批量操作,得绕开 JedisCluster 的自动路由,手动按 slot 分组 + 独立 pipeline。关键点在于:不依赖 JedisCluster.mget,而是自己算 slot、取连接、发 pipeline。
示例逻辑:
List<string> keys = Arrays.asList("user:{1}:name", "user:{1}:age", "order:{99}:id");
Map<integer list>> slotGroups = keys.stream()
.collect(Collectors.groupingBy(JedisClusterCRC16::getSlot));
for (Map.Entry<integer list>> entry : slotGroups.entrySet()) {
Integer slot = entry.getKey();
Jedis jedis = connectionHandler.getConnectionFromSlot(slot); // 注意:connectionHandler 需从 JedisCluster 提取
try (Pipeline p = jedis.pipelined()) {
for (String key : entry.getValue()) {
p.get(key);
}
List<object> results = p.syncAndReturnAll();
// 处理 results,注意其中可能含 JedisDataException(如 TRYAGAIN / ASK)
}
}
</object></integer></integer></string>
这样每组 key 都走确定 slot 的连接,彻底避开跨槽重定向问题。
容易被忽略的三个硬伤
即使你调大了 setMaxRedirections(32),以下问题依然存在:
-
JedisCluster初始化时若只传部分节点,cluster nodes返回的节点拓扑可能不全,导致 slot 映射缺失 - 集群扩容/缩容后,客户端不重启就不会主动拉取新
cluster slots,缓存长期 stale -
ASK场景下,JedisCluster不会像redis-cli -c那样自动重试到目标节点,而是原地 retry 旧节点
所以别迷信“调大阈值”,跨槽批量操作必须放弃 JedisCluster 的高层封装,回归 slot 粒度控制。










