必须显式调用close(),否则后台janitor goroutine不会随gc自动退出,导致内存和goroutine泄漏;反复new cache需确保前一个已close,全局复用更安全;set传0使用默认过期,传cache.noexpiration(即-1)才永不过期,传负值立即过期;get返回nil时须依赖bool参数判断是否存在。

Go-Cache 的生命周期管理必须显式调用 Close()
Go-Cache 启动后会自动启用后台 janitor goroutine 扫描过期项,但它不会随 cache 实例被 GC 自动退出。如果你在 HTTP handler、中间件或循环中反复 cache.New(),旧实例的 janitor 会持续运行并持有对 cache 的引用,导致内存泄漏和 goroutine 泄漏。
- 每次新建 cache 前,确认前一个是否已调用
cache.Close() - 全局复用是首选:在
init()或应用启动时初始化一次,注入到依赖中 - 若需动态生命周期(如 per-request 缓存),务必在 defer 或 cleanup 阶段调用
cache.Close(),否则 goroutine 可能残留数秒甚至更久 -
Close()不仅停止janitor,还会清空itemsmap 和stopchannel,避免残留状态干扰新实例
Set() 中传 0 和负数的语义完全相反
很多开发者误以为 -1 表示“永不过期”,结果写入即失效——这是最常踩的坑。Go-Cache 对 duration 的处理逻辑很直接:
-
Set(key, value, 0):使用 cache 实例的defaultExpiration(若未设则为永不过期) -
Set(key, value, cache.NoExpiration)或time.Duration(0):明确永不过期 -
Set(key, value, -time.Second)或任意负值:立即标记为过期,Get()永远返回found == false
注意:cache.NoExpiration 就是 -1,但它是预定义常量;直接传 -1 会被当作数值而非语义标识,行为等同于 -time.Nanosecond —— 即刻过期。
Get() 返回 nil 时,永远以第二个 bool 参数为准
Go-Cache 允许存入 nil 值(比如 *User 指针为 nil),所以不能靠 value == nil 判断是否存在:
- 正确写法:
value, found := cache.Get("key"); if !found { /* 真缺失 */ } - 错误写法:
if value == nil { /* 错!可能是存了 nil,也可能是没命中 */ } - 尤其在反序列化场景下,
json.Unmarshal可能产出nil结构体指针,若不检查found,缓存穿透风险陡增
清理间隔(cleanupInterval)设为
创建 cache 时第二个参数是清理间隔,不是过期时间:
-
cache.New(5*time.Minute, 10*time.Minute):默认 5 分钟过期,每 10 分钟扫描一次过期项 -
cache.New(5*time.Minute, 0)或cache.New(5*time.Minute, -1):过期机制仍生效,但janitor不启动 → 过期项只在Get()时惰性删除(即“读时淘汰”),内存不会主动释放 - 生产环境慎用
0:长时间运行后,内存中堆积大量过期但未清理的Item,最终 OOM - 若需精细控制,可手动调用
cache.DeleteExpired(),但要注意它会阻塞当前 goroutine
真正麻烦的是混合使用永不过期与有时效 key:即使 90% 的 key 设了 NoExpiration,janitor 仍要遍历全表判断,清理效率随数据量线性下降。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











