redis.NewClient初始化必须显式配置PoolSize(建议并发请求数的2–4倍)并用带超时ctx调Ping验证连通性,禁用context.Background,空值缓存用"null"+随机TTL防穿透,结构体字段须导出并加json tag。

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 就卡死,日志里只看到 redis: connection pool timeout,查不到真实根因。
- HTTP handler 中统一用
ctx, cancel := context.WithTimeout(r.Context(), 300*time.Millisecond),继承 request 生命周期 - 写操作(
Set、Del、分布式锁)同样要超时;锁逻辑里缺超时,会导致锁假死、资源长期被占 - 别在
init()里提前声明全局ctx = context.Background()——它不是“默认上下文”,是“无约束上下文”
Cache-Aside 模式下如何防穿透/击穿/雪崩
Redis 自身不解决这三类问题,Go 层必须自己兜底。布隆过滤器太重,多数场景用轻量策略更稳。
- 穿透:查 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()
结构体存 Redis 必须用 json 且字段全导出+带 tag
直接 json.Marshal(user) 存进去,json.Unmarshal([]byte(val), &user) 读出来却是空对象?八成是字段没导出或缺 json tag。
- 字段名必须首字母大写(Go 可导出),且显式加 tag:
ID int `json:"id"`,否则json包跳过该字段 - 禁用
gob——它只认 Go 进程,跨语言(Python/Node.js)或未来服务拆分后反序列化直接失败 -
time.Time别直接塞:统一转成int64时间戳再存,避免 JSON 默认 RFC3339 字符串和 gob 存纳秒整数的解析不一致 - 存之前先
json.Marshal试一下,检查是否报错,能提前发现 struct 定义问题
复杂点往往藏在初始化和空值处理里:PoolSize 不调、Ping 不做、context 超时不设、空值不缓存、struct 字段不导出——这些看似小动作,线上出问题时排查成本极高。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











