登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis Cluster 迁移 slot 时客户端为什么会收到 MOVED

来源:17golang原创

时间:2026-09-07 14:54:28 157浏览 收藏

Redis Cluster 迁移 slot 时看到 -MOVED 6380 10.0.0.12:6379,通常不是数据丢失,而是客户端连接的节点已经不是这个 slot 的当前 owner。关键在于:稳定状态下的 MOVED 表示应永久更新路由;迁移中间态的 ASK 只表示当前这一次命令临时去目标节点。把两者混为一谈,才会让重试和拓扑缓存越修越乱。

先按 key 算出 slot,再看该 slot 的真实归属;收到 MOVED 就刷新 slot 映射,收到 ASK 则只对当前命令发送 ASKING,不要提前把整个 slot 表改到目标节点。

要点速览
  • Redis Cluster 将 key 映射到 0~16383 的 hash slot,客户端通常缓存 slot 到节点的关系。
  • MOVED 是稳定归属变化或访问了错误节点的永久重定向;ASK 是迁移期间的单次重定向。
  • 客户端应支持 Cluster 模式,能处理 MOVED、ASK、ASKING、TRYAGAIN 和多 key 跨 slot 边界。
  • 排查时同时看 CLUSTER SLOTSCLUSTER SHARDSCLUSTER NODES 与客户端拓扑刷新日志。

为什么旧连接会拿到 MOVED

Redis Cluster 的 key 先经过 CRC16(key) mod 16384 得到 slot。集群稳定时,一个 slot 由一个主节点负责。客户端为了减少每次请求的路由成本,会在内存里保存一份 slot 表,但这份表可能因为 reshard、故障转移或刚启动而落后。

# 用集群模式连接,让客户端能够跟随 MOVED/ASK
redis-cli -c -h 10.0.0.11 -p 6379
# 查看业务 key 映射到哪个 slot
redis-cli -c -h 10.0.0.11 -p 6379 CLUSTER KEYSLOT user:{42}:profile
# 查看 slot 到节点的当前映射
redis-cli -h 10.0.0.11 -p 6379 CLUSTER SLOTS

假设客户端仍把 slot 6380 指向 Node-A,而集群配置已经把它交给 Node-B。请求先到 Node-A,Node-A 返回 MOVED 和 Node-B 的地址。正确的动作是更新本地 slot 表,让后续请求直接去 Node-B;只重试当前请求而不更新映射,会让每个请求都多一次往返。

Redis Cluster slot 6380 真实 owner 与客户端旧 slot 表不一致导致 MOVED
图1:slot 6380 的真实所有者已变为 Node-B,旧连接访问 Node-A 时会收到 MOVED,并应更新本地 slot 映射。

迁移中间态为什么先出现 ASK

reshard 并不是瞬间把所有 key 从源节点搬走。典型过程是:目标节点先把 slot 标记为 IMPORTING,源节点再标记为 MIGRATING,随后用 CLUSTER GETKEYSINSLOT 找出 key 并迁移。此时同一个 slot 的旧 key 可能仍在源节点,新 key 则应该在目标节点创建。

因此源节点遇到不存在的 key 时返回 ASK,告诉客户端“这一个命令去目标节点试试”。客户端连接目标节点后先发送一次 ASKING,再发送原命令;它不能因为一次 ASK 就把 slot 永久改指向目标节点,因为其他 key 仍可能留在源节点。

Redis Cluster MIGRATING 与 IMPORTING 状态下 ASK 和 ASKING 的单次路由关系
图2:迁移未完成时,ASK 只把当前命令带到 IMPORTING 节点;完成归属切换后才按新的 owner 永久改路由。

迁移结束后,协调方会向相关节点发送 CLUSTER SETSLOT slot NODE target-node-id,让目标节点成为正式 owner。此后再访问旧节点,收到的就是表示永久归属变化的 MOVED。也就是说,ASK 关注“这一次命令”,MOVED 关注“以后应该找谁”。

客户端该刷新什么,哪些错误不能盲目重试

新客户端可以使用 CLUSTER SHARDS 获取分片与节点信息;历史客户端常用 CLUSTER SLOTS。Redis 文档已将 CLUSTER SLOTS 标为 7.0 起 deprecated,新实现应确认客户端库是否支持 SHARDS,但这不等于旧集群会停止返回 MOVED。

返回含义客户端动作
MOVEDslot 的正式 owner 在另一节点,或当前节点不是 owner重试并刷新该 slot,必要时重新拉取完整拓扑
ASKslot 正在迁移,目标节点暂时接收当前命令只重试当前命令,先发 ASKING,不更新永久映射
TRYAGAIN多 key 命令遇到迁移中的部分状态短暂退避后重试,检查是否把多 key 操作拆到了不同 slot

排查时先记录完整错误行、slot、目标地址和客户端库版本,再在任意节点执行 CLUSTER NODES。输出中的 slot->-node-id 表示 migrating,slot- 表示 importing。若 MOVED 长时间重复出现,重点检查客户端是否以 Cluster 模式初始化、是否禁用了拓扑刷新、容器或代理是否把节点地址改写错,以及是否存在旧连接池缓存。

多 key 命令还要单独看 hash tag。把相关 key 写成 order:{100}:itemsorder:{100}:total,可以让相同花括号内容参与 slot 计算;但这只是让 key 进入同一 slot,不会替客户端处理迁移中的临时状态。

相关问题

收到 MOVED 后只重试当前请求可以吗?

可以暂时完成当前请求,但不应停在这里。客户端还要更新 slot 表,否则下一次仍访问旧节点,MOVED 会变成固定的额外往返。

ASK 后为什么必须发送 ASKING?

目标节点处于 IMPORTING 状态时,不带 ASKING 的普通请求会被拒绝并再次返回 MOVED。ASKING 是只对下一条命令生效的一次性许可。

redis-cli 为什么看不到 MOVED?

使用 redis-cli -c 后,命令行客户端会代为处理集群重定向。要观察原始返回,可连接单个节点并关闭 Cluster 模式,再结合 CLUSTER KEYSLOT 和 CLUSTER NODES 判断路由。

记住一条判断线就够了:归属已经确定变化,用 MOVED 更新拓扑;归属正在搬迁,用 ASK 只转发当前命令。把客户端初始化、重试策略和 slot 表刷新放在同一个 Cluster 语义里,迁移期间的日志就不再是“随机报错”,而是能解释的状态信号。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>