pipeline未写入redis的最常见原因是未调用exec(),命令仅缓存在内存中;必须显式调用exec()才能真正发送并执行。

没调 Exec() 就等于没发命令,Pipeline 不是自动触发的魔法,它只是把命令攒在内存里等你拍板。
为什么 Pipeline 看似执行了却没写入 Redis?
最常见原因是漏掉 Exec() 调用。写了十几条 Set()、ZAdd(),最后没调 pipe.Exec(ctx),命令就静静留在内存里,随 GC 回收,数据彻底丢失。
-
Exec()是唯一真正发包的入口;不调它,网络层零流量 -
Exec()返回的是整批命令的聚合错误(比如连接超时),不是单条成败判据 - 每条命令返回的
*redis.Cmd(如*redis.StringCmd)才是结果载体,必须显式调.Result()或.Val()才能取值 - 常见误写:
pipe.Set(); pipe.Get(); pipe.Exec(); fmt.Println(pipe.Get().Val())—— 最后那个Get()根本没进 Pipeline,也不会被执行
什么时候该用 Pipeline?而不是 MGET/MSET
Pipeline 解决的是网络往返开销,不是逻辑封装。它只在批量、独立、高 RTT 场景下才值得上。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 适用场景:缓存预热(启动塞几万条
user:1001)、排行榜批量写入(几十个ZAdd())、批量删除(Del()多个 key) - 不适用场景:只读 3 条、写 2 个字段;或命令间有依赖(比如用
Get()结果决定下一条Set()的 value) - 优先用原生命令:
MGET/MSET内部已做 slot 分发、更稳、更少出错;它们比手撸 Pipeline 更省心 - 量级门槛:单次 ≥10 条独立命令,且 RTT 成为主要瓶颈时,收益才明显
Redis Cluster 下 Pipeline 必须按 slot 分组
集群不允许跨 slot 打包命令。{user:123}:profile 和 {order:456}:meta 很可能落在不同节点,直接 rdb.Pipeline() 会被拒绝。
- 必须先算 slot:
slot := clusterClient.KeySlot("user:123") - 再查节点:
node := clusterClient.NodeForKey(slot) - 最后在该
*redis.Client上调node.Pipeline()—— 此时退化为单机行为 - 如果 key 数量大、分布散,手动分组 + 多个 Pipeline 更安全,但代码复杂度陡增
- 更省事的替代:用
MGET/MSET,它们由go-redis自动按 slot 路由并并发执行
超时、错误、大 key —— 三个最容易被忽略的坑
Pipeline 的失败模式和单条命令完全不同:一次发、一批回、错误分散在每个 Cmd 里,不是集中在 Exec()。
-
ctx超时对整批生效:设context.WithTimeout(ctx, 100ms),哪怕第 1 条卡住,后面所有都直接中断,不会重试 - 错误不是“全成功”或“全失败”:可能前 5 条 ok,第 6 条
ERR wrong number of arguments,第 7–10 条仍照常执行;必须遍历每个cmd.Err() - 大 key 会拖垮整个 Pipeline:单个
SET带 1MB value,会阻塞整批命令的序列化与发送;建议提前拆分或改用SCAN+分片
真正难的不是写 Pipeline,而是判断哪条命令该进、哪条不该进,以及怎么把错误检查嵌进业务逻辑里——这些细节不处理,吞吐量上不去,反而埋下静默失败的隐患。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










