MOVED报错非集群故障,而是客户端拓扑缓存过期或配置不当的明确信号;需显式设置maxRedirects为3~5、仅更新变动槽位映射、区分MOVED与ASK语义、禁用重定向路径中同步刷新拓扑。

MOVED重定向不是集群故障,而是客户端拓扑缓存过期或配置不当的明确信号——90%的高频重定向问题,根源在客户端没设maxRedirects或刷新策略失控。
为什么maxRedirects必须显式设为3~5
默认值(如redis-py的16、lettuce的5)在槽迁移期间极易触发重试风暴。设太高会卡在错误节点反复跳转;设太低(如1)又会误判正常迁移中的二次MOVED(源节点返回一次,目标节点刚接管未稳再返回一次)。
-
redis-py:初始化RedisCluster时传max_redirections=3 -
lettuce:用ClusterClientOptions.builder().maxRedirects(3) -
go-redis:ClusterOptions.MaxRedirects = 3 - 关键点:
ASK重试不计入该计数器——所以设3不会阻断正在迁移的槽
收到MOVED后只刷新变动槽位,别全量覆盖
很多客户端(尤其旧版redis-py-cluster)一见MOVED就调CLUSTER SLOTS全量拉取,结果把其他健康槽的映射也刷掉了,反而扩大路由错误范围。
- 正确做法:解析
MOVED 1234 10.0.1.5:7001中的槽号和地址,仅更新slot 1234的映射 - 临时连接必须带认证和TLS配置,否则连不上目标节点,拓扑卡死在旧状态
- 拓扑缓存建议加≤5分钟过期,但扩缩容后需主动触发刷新,不能只等超时
禁止在重定向路径中同步刷新拓扑
有些客户端(如开启reinitialize_steps=1的redis-py)会在每次MOVED后立刻发CLUSTER SLOTS,而此时目标节点可能尚未完成Gossip同步,返回的仍是旧拓扑,形成嵌套重定向循环。
- 应改为:首次
MOVED快速重试一次;连续2次失败再触发刷新 - 生产环境务必关闭
log_redirections=True,避免日志淹没真实重试链路 -
CLUSTER SLOTS返回的是快照,本身就有延迟——它不保证与当前路由状态实时一致
mget、pipeline跨槽操作必然触发重定向
Redis Cluster协议强制要求多key操作必须同槽,mget user:1 name user:2 age只要两个key落在不同槽,整个命令就会陷入MOVED→重试→再MOVED循环,直到超限抛JedisClusterMaxRedirectionsException。
- 安全做法:手动按
JedisClusterCRC16.getSlot(key)分组,每组用独立Pipeline发往对应节点 - 哈希标签(
{user:1})可强制key落同一槽,但无法解决业务天然分散的场景 - 别依赖
setMaxRedirections(32)硬扛——这只会掩盖根本问题,且放大网络抖动影响
真正难调的不是参数,是客户端对MOVED和ASK的语义混淆:前者要改映射,后者只许临时借道。一旦把ASK 1234 10.0.1.6:6379当MOVED处理并更新本地槽表,后续所有请求都会打错节点,且错误会持续到下一次真正的MOVED发生。


















