必须设poolsize和ping验证,因默认poolsize=10易致连接池超时,且不主动ping会延迟暴露连通问题;poolsize应为并发请求数2–4倍并配合带超时ctx验证,进程退出前须close()防fd泄漏。

redis.NewClient 初始化必须设 PoolSize 和 Ping 验证
直接用 redis.NewClient 初始化再调 Set/Get 能跑通,但线上大概率连接耗尽或超时卡死。根本原因不是 Redis 不行,而是 Go 层没管好连接生命周期。
默认 PoolSize 是 10,小流量没问题;QPS 上千时会频繁阻塞在 pool.Get,报 redis: connection pool timeout。更糟的是,不主动 Ping(),首次 Get() 失败才暴露连不上 Redis。
-
PoolSize建议设为预估并发请求数的 2–4 倍(例如 QPS 200,设 40–80),但不超过 Redis 的maxclients(默认 10000) - 必须在 init() 或启动阶段用带超时的 ctx 调
rdb.Ping(ctx).Err(),别等线上才发现配置错了 - 进程退出前务必调
rdb.Close(),否则 fd 持续增长,最终触发too many open files
限流键设计要防穿透 + 随机 TTL
用户查一个永远不存在的 user_id=999999999,缓存没命中、DB 也没数据,下次还来——这就是穿透。直接返回 redis.Nil 后查 DB 是危险的,得在 Go 层拦截。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
查缓存返回 redis.Nil 时,不要立刻查 DB,先 rdb.Set(ctx, key, "null", baseTTL+jitter),值固定用字符串 "null"。
-
jitter必须每次生成:用rand.New(rand.NewSource(time.Now().UnixNano())),不能全局复用 seed,否则重启后所有 key 还是同时过期 - 业务层读到
"null"要显式判断:if val == "null" { return nil, nil },不然会当成真实数据解包 - 小项目别一上来就上布隆过滤器——空值缓存 + 短 TTL(如 1–2 分钟)更可控
结构体序列化必须导出字段 + json tag
直接 json.Marshal(user) 存进去,json.Unmarshal([]byte(val), &user) 读出来却是空对象?八成是 struct 字段没导出或缺 json tag。
- 字段名必须首字母大写(Go 可导出),且显式加 tag:
ID int `json:"id"`,否则json包跳过该字段 - 禁用
gob——它只认 Go 进程,跨语言(Python/Node.js)或未来服务拆分后直接反序列化失败 - 存之前先
json.Marshal试一下,检查是否报错,能提前发现 struct 定义问题 -
time.Time别直接塞:统一转成int64时间戳再存,避免 JSON 默
限流逻辑别用 context.Background()
所有 Redis 操作都该复用 request context 或至少加 WithTimeout。用 context.Background() 在 handler 里会导致超时无法传递、goroutine 泄漏、连接池卡死。
- 比如限流检查:
ctx, cancel := context.WithTimeout(r.Context(), 500*time.Millisecond),然后传给rdb.Get(ctx, key) - 注意:
cancel()必须在函数退出前调用,否则上下文泄漏 - 如果只是做兜底限流(如风控类低频操作),可用
context.TODO(),但绝不该用Background()
time.Time 的序列化方式——前者导致大量 key 同时过期引发雪崩,后者在跨服务解析时静默失败,日志里甚至不报错。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










