必须用redis.NewClusterClient()而非redis.NewClient(),因后者不处理MOVED/ASK重定向;常见原因包括误用单机客户端、初始化传入从节点地址、集群总线端口不通,且Addrs需填2–3个主节点地址并配置RouteByLatency、MaxRedirects等参数。

用 redis.NewClusterClient(),别用 redis.NewClient() —— 后者根本不会处理 MOVED 或 ASK 重定向,一发命令打错节点就直接报错。
为什么连集群总报 MOVED/ASK 错误
不是你的 key 写错了,是客户端压根没走集群协议路径。常见原因有三个:
-
redis.NewClient()被误用于集群地址 → 它当单机连,收到MOVED 12345 10.0.1.5:6379就原样返回 error,不重试也不跳转 - 初始化时传了从节点(replica)地址给
redis.NewClusterClient()→ 它会尝试向该节点发CLUSTER SLOTS,但从节点默认不响应这个命令,直接初始化失败 - 集群总线端口不通(比如 client 端口是 6379,总线应为 7379)→
CLUSTER SLOTS请求超时或静默失败,客户端卡在拓扑发现阶段
初始化 ClusterClient 必须设的几个配置项
只填 Addrs 不够,这几个字段漏一个,运行时都可能随机出问题:
-
RouteByLatency: true或RouteRandomly: true必须显式设一个。不设的话某些版本 fallback 到固定节点,导致负载严重倾斜 -
MaxRedirects: 8建议显式设置。默认是 -1,拓扑剧烈变更时可能陷入无限重定向循环 -
ReadOnly: false如果业务要写,这个一定不能漏。设成true后所有SET类命令直接返回MOVED错误,而不是自动重试 -
Addrs至少填 2–3 个主节点地址(如[]string{"10.0.1.1:6379", "10.0.1.2:6379", "10.0.1.3:6379"}),不能只写一个 —— 客户端不会自动轮询列表里下一个地址,第一个连不上就起不来
GET 能跑通,但 Pipeline 报 CROSSSLOT 怎么办
这不是 bug,是 Redis Cluster 协议硬限制:Pipeline 是 TCP 层打包机制,而集群要求每个命令先算 slot 再路由,无法统一打包跨 slot 的操作。
立即学习“go语言免费学习笔记(深入)”;
-
MGET、DEL这类多 key 命令,必须保证所有 key 落在同一 slot,否则服务端直接返回redis: CROSSSLOT Keys in request don't hash to the same slot - 强制同 slot 的方法是用哈希标签,例如
{user:1001}:profile和{user:1001}:settings→ 大括号内内容决定 slot,同一业务数据自然聚在一起 - 真要批量操作不同 slot 的 key,得先用
client.KeySlot(key)分组,再对每组 slot 单独取节点(client.Nodes())、建单机*redis.Client、调Pipeline() - 优先用原生命令替代 Pipeline:比如批量读用
MGET,批量写用MSET,它们内部已做 slot 校验和分发
Lua 脚本执行要注意 key 分布
脚本里涉及多个 key 时,Redis Cluster 要求所有 KEYS[...] 必须落在同一 slot,否则直接拒绝执行。
- 调用
script.Run(ctx, client, keys, args)前,确保keys切片里所有 key 都带相同哈希标签,例如[]string{"{order:123}:items", "{order:123}:status"} - 如果脚本逻辑必须操作不同 slot 的 key(比如跨用户迁移),只能拆成多个脚本分别执行,或改用服务器端分片协调逻辑
-
EVAL和EVALSHA在集群模式下不支持无 key 参数的调用(即KEYS为空),会报错NOREPLICAS Not enough good replicas to write或类似提示
最易被忽略的一点:集群节点间网络必须双向互通,不只是客户端能连上去。拓扑发现、心跳、重定向跳转都依赖节点之间的通信,防火墙或 Kubernetes NetworkPolicy 挡住总线端口(通常是 client port + 1000)时,错误表现往往很隐蔽——连接不报错,但后续任意命令都失败。


















