必须用 redis.Client,因其内置连接池、自动重连、线程安全;禁用 redis.Dial/redigo.Dial 和 Conn,避免连接泄露、超时堆积;初始化需设 PoolSize、Timeout 等参数;所有调用必须传 context;client.Close() 仅进程退出前调用一次。

必须用 redis.Client,别碰 redis.Conn 或 redigo.Conn —— 后者手动管理连接极易泄露、超时堆积、压测崩盘。
为什么不能用 redis.Dial 或 redigo.Dial
老项目里还能看到 redis.Dial() 或 redigo.Dial() 配合 Do() 手动发命令的写法,现在必须淘汰。这类方式要求每次调用后显式 Close(),但 panic、提前 return、HTTP 超时中断都会导致连接漏关;连接池也得自己实现,稍有疏忽就出现 too many open files 或 read tcp ...: i/o timeout。
-
redis.Client内置连接池、自动重连、线程安全,所有操作方法(Set()、Get()、HGetAll())都可并发调用 -
redigo的Conn是单次绑定,不支持复用,且无上下文控制能力,无法响应请求取消 - Go 生态当前事实标准是
github.com/redis/go-redis/v9(注意不是旧版go-redis/redis/v8),v9 已全面拥抱context.Context
初始化 client 时必须配哪些选项
只传 Addr 是裸奔。默认 PoolSize = 10、Timeout = 0(无限等待),线上一抖就卡死。
-
PoolSize:建议设为QPS × 平均延迟(秒)× 1.5,例如 200 QPS × 0.04s × 1.5 ≈ 12 → 实际取 32 更稳妥 -
Timeout、ReadTimeout、WriteTimeout:必须全设,比如5 * time.Second,否则网络延迟突增时 goroutine 永久阻塞 -
MinIdleConns:设为PoolSize / 2,避免冷启动后首次请求慢 -
MaxConnAge:设为30 * time.Minute,防止 NAT 或防火墙静默断开长连接 -
ClientName:加服务名+版本,如"user-service-v2.1",方便CLIENT LIST定位来源
所有 Redis 调用必须传 context
硬写 context.Background() 是定时炸弹。HTTP handler 里必须用 r.Context(),否则用户关浏览器、API 超时、K8s probe 失败,Redis 请求还在后台跑着,吃连接、占资源、拖垮整个服务。
立即学习“go语言免费学习笔记(深入)”;
-
client.Get(ctx, "key")、client.Set(ctx, "key", val, ttl)等所有方法第一个参数都是ctx - 不要在函数开头定义全局
var ctx = context.Background(),除非是 init 或 main 初始化测试 - 需要带超时?用
context.WithTimeout(r.Context(), 3*time.Second),别用固定 timeout 覆盖原 context 的 cancel 信号 - 遇到
redis.Nil错误要显式判断,而不是直接if err != nil就 panic —— key 不存在是正常流程
client.Close() 只需在进程退出前调用一次
不是每次请求结束都要关 client,更不是每次 Get() 后 Close 连接 —— redis.Client 是长生命周期对象,应全局复用。
- 在
main()函数末尾defer client.Close()即可 - 如果用的是封装结构体(如
type RedisClient struct { client *redis.Client }),Close 方法只需转发调用client.Close() - 忘记
Close()不会导致 immediate 泄露,但进程退出时未释放连接池会触发 warning 日志,且无法优雅等待连接归还 - 单元测试中若新建 client,务必在 test 函数末尾
client.Close(),否则并发测试容易端口耗尽
最常被忽略的是 MaxConnAge 和 context 传递链路完整性——前者让连接定期刷新避开中间设备断连,后者确保每一次 Redis 调用都服从上层请求生命周期。这两点不落实,再好的连接池配置也扛不住真实流量。


















