recover 对 Redis 错误完全无效,因为 go-redis 所有操作(包括 pipe.Exec())均通过返回 error 表达失败,从不 panic;需显式检查 execErr 和各 cmd.Err(),redis.Nil 是合法业务错误而非异常。

recover 不能捕获 Redis 管道执行异常。它只对 panic 生效,而 pipe.Exec(ctx) 和所有 go-redis 命令返回的都是 error,不是 panic。
为什么 recover 对 Redis 错误完全无效
go-redis 的设计严格遵循 Go 的错误处理约定:所有操作(包括 pipe.Exec())都通过返回 error 表达失败,绝不会主动 panic。网络超时、MOVED 重定向、CROSSSLOT、连接池耗尽等——全走 error 分支。你写 defer func(){ if r := recover(); r != nil { ... } }() 包住 pipe.Exec(),永远进不去。
真正该检查的两个 error 位置
Redis 管道执行涉及两层错误:
-
pipe.Exec(ctx)返回的 error:代表整个管道提交失败,通常是网络级问题(如连接中断、上下文取消、协议解析失败) - 每个命令 cmd 的
cmd.Err():代表单条命令在服务端执行失败,比如 key 类型错误、redis.Nil、ERR CROSSSLOT
必须分开处理:
立即学习“go语言免费学习笔记(深入)”;
pipe := client.Pipeline()
pipe.Set(ctx, "a", "1", 0)
pipe.HGetAll(ctx, "b") // b 是 string 类型,HGetAll 会报错
_, execErr := pipe.Exec(ctx)
if execErr != nil {
// 处理网络/协议层错误,比如 context.DeadlineExceeded 或 redis: connection closed
}
// 即使 execErr == nil,也要逐个检查 cmd.Err()
for i, cmd := range []*redis.Cmd{pipe.Cmds()[0], pipe.Cmds()[1]} {
if cmd.Err() != nil {
// 第 1 条成功,第 2 条报 (error) WRONGTYPE Operation against a key holding the wrong kind of value
}
}
集群模式下最容易漏掉的错误点
用 redis.NewClusterClient() 时,pipe.Exec() 不会 panic,但可能静默失败:
- 没调
pipe.Exec()→ 所有命令丢失,无任何 error 提示 - 对不同 slot 的 key 混用一个 pipeline →
execErr是redis: CROSSSLOT,但你若只看execErr而忽略后续 cmd 的Err(),就错过具体哪条命令越界 - 分组后并发执行多个 pipeline,但某个
node.Pipeline().Exec(ctx)忘了加 error 检查 → 该组结果全丢,且不报错
关键动作永远是:显式调 Exec() + 显式遍历 Cmds() + 显式查每个 cmd.Err()。
别把 redis.Nil 当成异常来 recover
redis.Nil 是合法的业务态 error,比如 rdb.Get(ctx, "missing").Err() == redis.Nil。它不是故障,而是“查无此键”。用 errors.Is(err, redis.Nil) 判断即可。试图用 recover 拦截它,既做不到,也违背语义。


















