必须显式配置PoolSize(20–50)、MinIdleConns(≥5)并调用带超时ctx的Ping验证连通性,所有Redis操作须用context.WithTimeout,Key加服务前缀且Value统一json序列化。

redis.NewClient 初始化必须显式配 PoolSize 和 Ping 验证
直接 redis.NewClient 后就塞进 Echo 的 middleware 或 handler 里用,线上压测大概率出现 redis: connection pool timeout 或首请求卡死 2 秒——这不是 Redis 慢,是连接池没水了,也没验通路。
PoolSize 默认是 10,MinIdleConns 是 0;QPS 超 200 就开始排队;冷启动时若空闲连接被 SLB/NAT 静默断开,第一个 Get 必重连失败。
- PoolSize 设为 20–50:低于 20 容易阻塞;高于 50 可能打满 Redis 的
maxclients(单实例建议 ≤5000) - MinIdleConns ≥5:防中间设备断连后首请求 hang 住
- 初始化后立刻调
rdb.Ping(ctx).Err(),ctx 必须带超时(如context.WithTimeout(context.Background(), 2*time.Second)),别等第一次Get才暴露地址错、密码错或网络不通 - 地址、密码、DB 编号从环境变量读(如
os.Getenv("REDIS_ADDR")),别硬编码"localhost:6379"
Echo 中所有 Redis 操作必须带 context.WithTimeout
在 Echo 的 HTTP handler 里传 context.Background() 给 client.Get 或 client.Set,等于把 goroutine 生死交给 Redis 响应时间。它卡 2 秒,你的 handler 就卡 2 秒,监控里只看到 goroutine 数暴涨、QPS 断崖下跌。
- HTTP handler 中统一用:
ctx, cancel := context.WithTimeout(c.Request().Context(), 300*time.Millisecond),然后 defer cancel() - 写操作(
Set、Del、SetNX)同样要超时;分布式锁逻辑里缺超时,会导致锁假死、后续请求永久等待 - 别在
init()里声明全局ctx = context.Background()——它没有取消信号,不是“默认”,是“永生”
Key 加前缀 + Value 统一 json.Marshal
多个服务共用一个 Redis 实例但 key 没前缀,或同一结构体在不同模块混用 json.Marshal 和 gob.Encode,结果就是缓存读出来 invalid character 或直接 panic,日志里找不到源头。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
立即学习“go语言免费学习笔记(深入)”;
- key 推荐格式:
"user:profile:" + uid、"order:summary:" + time.Now().Format("20060102") + ":uid_" + uid - 拼 key 时避免裸接用户输入,先做
strings.ReplaceAll(username, ":", "_")或url.PathEscape - 结构体字段必须导出 +
json:"field"tag;存取都走json.Marshal/json.Unmarshal,别直接传 struct 指针给Set——序列化失败会静默丢数据 - TTL 别设成 0:
Set(ctx, key, val, 0)表示“不设置过期”,不是“永不过期”,更不是“过期时间为 0 秒”
Cache-Aside 模式下必须自己兜底穿透/击穿/雪崩
Redis 自身不解决这三类问题,Echo 层必须在业务代码里加轻量策略。布隆过滤器太重,多数场景靠这几条就够了。
- 穿透:查 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()
最常被忽略的是:连接池参数和超时控制不是“可选项”,是上线前必须对齐的硬门槛;而 "null" 空值缓存和 singleflight 不是锦上添花,是防止 DB 被打穿的第一道真实防线。


















