Redis Cluster 不支持跨 slot Pipeline,必须按 slot 分组在对应节点执行;MGET 是原生命令,优先用于同 slot 批量读取,超 5000 key 需分批,返回值含 nil 需谨慎类型断言。

Redis Cluster 不支持跨 slot 的 Pipeline
直接对 *redis.ClusterClient 调用 Pipeline() 会 panic,go-redis/v9 明确禁止该操作。集群模式下,Pipeline 必须按 slot 分组后,在对应节点的 *redis.Client 上执行——它本质退化为多个单机 Pipeline,而非“一个管道打遍全集群”。
典型错误现象:ERR CROSSSLOT Keys in request don't hash to the same slot,哪怕只混入两个 key(如 "user:1001" 和 "order:2002"),只要算出的 slot 不同,就会被拒绝。
- slot 计算公式固定:
CRC16(key) % 16384,可用clusterClient.KeySlot(key)获取 - 查节点不能靠 guess:必须调用
clusterClient.NodeForKey(slot)拿到实际*redis.Client - 别手动写 CRC16 实现——go-redis 内置函数已处理字节序和边界,复用更安全
批量读取应优先用 MGET,而非手写 Pipeline
对同一 slot 的一批 key 批量读取,MGET(keys...) 是原生命令,内部已做 slot 校验与路由,比手写 Pipeline 更稳、更省事,且天然兼容集群。
只有当你要混合不同命令(比如同时 MGET + EXPIRE + ZADD)且保证它们落在同一 slot 时,才需自己建 Pipeline;纯读场景几乎没理由绕开 MGET。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
立即学习“go语言免费学习笔记(深入)”;
-
MGET返回值是[]interface{},nil 元素表示 key 不存在,类型断言要小心 - 如果 key 数量超 5000,建议拆成多批——Redis 单次响应体过大易触发
proto-max-bulk-len限制或客户端 buffer 溢出 - 不要在
MGET前加context.WithTimeout过短(如
真要手写 Pipeline,必须分组 + 并发执行
对 3000 个 key 做批量读取,先按 slot 分组,再对每组并发调用 node.Pipeline(),否则性能反而比串行差——单线程 Redis 处理大包请求时,会阻塞其他请求,引发延迟毛刺。
- 分组逻辑不能用 map[int][]string 简单聚合:要确保每个 group 的 key 数量均衡(比如每组 100–300 条),避免某节点负载过重
- 并发数别盲目设高:通常 2–4 个 goroutine 足够,再多会加剧连接竞争,尤其在连接池较小(
MinIdleConns - 每个 group 的
pipe.Exec(ctx)必须单独调用,且只能调一次;重复调用 panic,漏调则命令静默丢失 - 结果收集要用 channel 或 sync.WaitGroup,别用全局变量或闭包捕获循环变量
错误处理必须落到每条命令,不能只看 Exec()
pipe.Exec(ctx) 返回的 err 仅代表网络层失败(连接断、超时),不代表某条 Get() 成功。例如对一个 string 类型 key 执行 HGet(),Exec() 仍返回 nil,但对应 *redis.StringCmd 的 Err() 是 redis.Error("WRONGTYPE ...")。
- 所有
pipe.Get(ctx, key)调用后,必须保存返回的*redis.StringCmd指针 -
Exec()后逐个调cmd.Result()(读操作)或cmd.Err()(写操作),Result()内部已合并.Val()和.Err() - 数值类命令(如
Incr())必须用.Int64(),别用.Val(),否则类型断言 panic - 别把错误聚合进一个 slice 后统一处理——定位不到具体是哪个 key 出错,线上排查成本陡增
实际中最容易被忽略的点:你写的“批量读取”函数是否真的发出了请求? 漏掉 pipe.Exec(ctx),或者在 Exec() 前做了 HTTP 请求、数据库查询,Pipeline 的 RTT 优化就彻底失效了。

















