go-cache 是单机内存缓冲层,不能替代 redis 或用于分布式缓存;需正确设置 ttl 和 cleanupinterval,get() 要检查 bool 返回值,配合 singleflight 防击穿,空值也需缓存以避免穿透。

go-cache 不是 DB 压力的“解药”,而是单机内存里的临时缓冲层;它不能替代 Redis,也不能跨实例同步——直接拿它当分布式缓存用,DB 压力只会更大,还伴随配置漂移、重启丢失、空值穿透等一连串问题。
cache.New() 传错参数就等于埋雷
cache.New() 必须传两个 time.Duration:第一个是默认过期时间,第二个是清理间隔。传 0 或负数会 panic,设成 0 还会让过期项永远不删,内存只增不减。
-
cache.NoExpiration才表示永不过期,0≠ 永不过期,而是触发内部转换失败 -
cleanupInterval必须 > 0;设为0不是“不清理”,而是根本不起 goroutine,过期 key 永远滞留 - 推荐组合:
cache.New(30<em>time.Second, 5</em>time.Second)(短 TTL + 频繁扫描),尤其适合热点配置兜底
Get() 返回 (nil, true) 不代表没值
Get() 返回的是 (interface{}, bool),第二个布尔值才是“是否命中有效条目”。很多人只看第一个返回值,结果:
- 存了
nil *User,Get()返回(nil, true),误判为“未命中”导致反复查 DB - 存了
map[string]int后修改原 map,缓存里内容跟着变(因为存的是引用,不是拷贝) - 正确写法永远是:
if val, found := cache.Get("key"); found { /<em> 处理 val </em>/ }
高并发下必须加 singleflight 控制加载
多个 goroutine 同时发现本地缓存 miss,会一起打 Redis 和 DB —— 这就是缓存击穿。Go-Cache 自身不提供加载互斥,得靠外部控制:- 用
singleflight.Group包一层,key 统一用"config:" + key格式 -
sg.Do("config:rate_limit", func() (interface{}, error) { ... })确保同一 key 只有一路加载 - 别在
init()里预热:此时*cache.Cache实例还没构造好,调用Get()直接 panic
空值不缓存 = 反复穿透 DB
查一个不存在的开关(比如新功能灰度开关还没配),如果每次都不缓存空结果,就会持续穿透到 Redis 和 DB:- 对空结果也调
Set("switch:foo", "<strong>MISSING</strong>", 5*time.Second),用特殊标记占位 - TTL 要比正常值短得多(比如 5s vs 30s),避免无效占位太久
- 注意:Redis 层也要做同样空值缓存,否则 Go-Cache 过期后仍会穿透
真正压不住 DB 的,往往不是缓存没加,而是本地缓存和远程缓存没对齐、空值没兜住、并发加载没控住——这些点漏掉一个,缓存就变成放大器,而不是减速带。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











