redis.newclient初始化必须显式配置poolsize并执行ping验证,否则线上易报连接池超时或首次请求卡死;poolsize建议设为预估qps的2–4倍,启动阶段需用带超时ctx调用rdb.ping(ctx)检验连通性,并统一为所有操作添加context超时控制。

redis.NewClient 初始化必须显式配 PoolSize 和 Ping 验证
直接 redis.NewClient 后就调 Set/Get,线上大概率报 redis: connection pool timeout 或首次请求卡死——根本不是 Redis 慢,而是连接池没调、连通性没验。
-
PoolSize默认是 10,QPS 超 200 就开始排队;建议设为预估并发请求数的 2–4 倍(如 300 QPS → 设 60–120),但别超 Redis 的maxclients(默认 10000) - 必须在启动阶段用带超时的
ctx调rdb.Ping(ctx).Err(),否则第一次Get失败才暴露地址错/密码错/网络不通 - 别硬编码
"localhost:6379",从环境变量(如REDIS_ADDR)或配置中心读;微服务部署下,这个不改,上线必炸 - 所有连接要设
MinIdleConns: 5和MaxConnAge: 30 * time.Minute,防中间设备静默断连导致后续请求 hang 住
所有 Redis 操作必须带 context.WithTimeout
用 context.Background() 或不带超时的 context.TODO() 调 client.Get,一旦 Redis 响应慢或网络抖动,goroutine 就卡死,日志里只看到模糊的 context deadline exceeded,根本看不出是 Redis 拖垮的。
- HTTP handler 中统一用
ctx, cancel := context.WithTimeout(r.Context(), 300*time.Millisecond),300ms 是缓存场景合理上限 - 写操作(
Set、Del、分布式锁)同样要超时;锁逻辑里缺超时,会导致锁假死、资源长期被占 - 禁止在
init函数里声明全局ctx = context.Background()——它没有取消能力,也不是“默认上下文”
Key 设计和结构体序列化最容易踩坑
本地调试只看 Go 日志里的 val, err := rdb.Get(ctx, "user:1001").Result() 返回值,会掩盖真实问题。终端执行 redis-cli -p 6379 手动查,才能暴露本质:
-
TYPE user:1001确认是不是 string;若返回 hash 却用Get(),会报WRONGTYPE Operation against a key holding the wrong kind of value -
TTL user:1001查是否真有过期时间;很多“缓存没生效”其实是 TTL 设成 0 或负数,Redis 当即删掉 key - 结构体存 JSON 时,确保字段全导出 + 显式
json:"name"tag;否则json.Marshal成功但json.Unmarshal解包失败,值为空对象 - 所有缓存键必须带服务前缀,例如
"user-service:user:123",避免跨服务键冲突
穿透/击穿/雪崩必须在 Go 层兜底,Redis 不管这事
这三类问题不是靠加锁或布隆过滤器就能一劳永逸解决的,得结合业务粒度选轻量手段:
- 穿透:查 DB 返回空,缓存层要存
rdb.Set(ctx, key, "null", 60*time.Second);业务解包前先判if val == "null" { return nil, nil } - 击穿:热点 key 过期瞬间大量请求打穿 DB,用
singleflight.Group包一层Do,同一 key 的并发请求只放行一个去查 DB - 雪崩:避免所有 key 同一时刻过期,TTL 加随机扰动:
baseTTL + time.Duration(rand.Int63n(int64(2*time.Minute)));每次生成前用time.Now().UnixNano()做 seed,别用全局rand.Seed()
最常被忽略的是:缓存操作必须封装成服务接口,而不是散落在 handler 里反复写 json.Marshal + rdb.Set;结构体字段 tag 不一致、空值没兜底、key 拼错前缀,这些错误在压测时才集中爆发,但根因都在初始化和封装层漏掉了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











