go-redis默认不自动重连,NewClient()仅拨号一次,连接断开后因连接池无法重建而持续返回redis: nil;需配置MaxRetries、健康检查等策略。

go-redis 默认不自动重连,连接断开后会持续报 redis: nil 或 connection refused 错误,必须显式配置重连策略或手动恢复。
为什么 redis.NewClient() 连不上就一直失败?
默认情况下,redis.NewClient() 创建的客户端只做一次拨号(dial),后续命令复用底层连接;一旦网络抖动、Redis 重启或连接超时,连接对象就进入“已关闭但未重建”状态,所有操作返回 redis: nil ——这不是空值错误,而是连接池里拿不到可用连接。
常见错误现象:
- 程序运行几小时后突然所有
Get/Set返回redis: nil -
ctx.Done()触发后没清理连接,下次再用同个*redis.Client实例直接 panic - 本地开发连得上,部署到 K8s 后频繁报
dial tcp: i/o timeout,但没重试
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须设置
MaxRetries:在redis.Options中配MaxRetries: 3,它控制单条命令的重试次数(仅限幂等命令,如GET) - 启用连接池健康检查:
MinIdleConns: 5+MaxConnAge: 30 * time.Second,让老连接定期退出,避免僵死 - 别依赖
client.Ping()做“心跳”——它只测当前连接,不触发重连;真要保活,得结合client.PoolStats()看IdleConns是否归零
context.WithTimeout 和 client.SetCtx 到底该用哪个?
go-redis 所有命令都接收 context.Context 参数,但行为和你直觉可能相反:client.SetCtx() 是给整个客户端设默认上下文(极少用),而每个命令调用时传入的 ctx 才真正控制本次操作的超时与取消。
使用场景:
- HTTP handler 中用
ctx, cancel := context.WithTimeout(r.Context(), 100*time.Millisecond),然后传给client.Get(ctx, key) - 后台任务需长超时(如导出缓存),单独建新
context.WithTimeout(ctx, 5*time.Minute),不污染主流程
容易踩的坑:
- 误调
client.SetCtx(ctx)后,所有命令都继承这个 ctx —— 如果它提前 Done,整个客户端就废了 - 传入已 cancel 的 ctx(比如 defer cancel() 后又调用 client),命令立刻返回
context canceled -
ctx.WithDeadline时间设得太紧,Redis 慢查询还没返回就被中断,日志里全是context deadline exceeded
Pipeline 和 TxPipeline 什么时候用?性能差多少?
两者都批量发命令,但语义完全不同:Pipeline 是客户端合并请求、服务端逐条执行(无事务);TxPipeline 是 Redis 的 MULTI/EXEC,要么全成功,要么全失败。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 读多写少、允许部分失败的场景(如批量查用户头像),用
client.Pipeline(),吞吐能提 3–5 倍 - 涉及金额、库存扣减等强一致性操作,必须用
client.TxPipeline(),否则并发下会超卖 - 注意
TxPipeline失败时返回的是*redis.TxFailedErr,不是普通 error,要用errors.Is(err, redis.TxFailedErr)判断 - 别在 Pipeline 里混用读写命令还指望顺序——Redis 不保证 pipeline 内响应顺序,
SET和GET的结果可能错位
最常被忽略的一点:go-redis 的 client.Close() 不是可有可无的收尾动作。它会阻塞等待连接池清空,如果程序 exit 前没调,可能导致 fd 耗尽或 Kubernetes liveness probe 失败。哪怕用 defer client.Close(),也要确认 defer 确实被执行到了——比如 panic 后 recover 了但没再 close,连接就泄漏了。


















