Go-Cache 不适合长期存储或高并发写入,因其仅依赖过期+定时清理,无容量限制易OOM;sync.Map不支持自动过期、LRU淘汰和OnEvicted回调,无法满足时效性缓存需求。

Go-Cache 不适合长期存储或高并发写入场景,它本质是带过期机制的线程安全 map,用对了能省事,用错了反而引入竞争或内存泄漏。
为什么不能直接用 sync.Map 替代 Go-Cache?
因为 sync.Map 不支持自动过期、LRU淘汰和清理回调。Go-Cache 的核心价值就在这三点:
-
NewCache创建时可设默认过期时间,每个Set还能单独覆盖 -
OnEvicted回调让你在 key 被删前做资源释放(比如关闭文件句柄、归还连接) - 后台 goroutine 定期扫描过期项,但不会阻塞读写 —— 这个“懒清理”策略是性能关键
如果你只想要并发安全的 map,sync.Map 更轻量;但只要涉及“临时”“有时效”,Go-Cache 的语义更清晰。
Get 返回 nil 时,到底是没命中还是值本身就是 nil?
Go-Cache 的 Get 方法返回 interface{} 和 bool,第二个参数才是判断依据:
立即学习“go语言免费学习笔记(深入)”;
value, found := cache.Get("user:123")
if !found {
// 确实不存在,不是值为 nil
}
常见错误是只看 value == nil 就认为缓存未命中,结果把合法存入的 nil 值(比如空结构体指针)当成缺失处理。永远依赖 found。
如何避免定时清理 goroutine 泄漏?
Go-Cache 默认启用后台清理,但如果你反复创建新 cache 实例(比如在 HTTP handler 里每次请求都 newCache()),旧 cache 的清理 goroutine 不会自动退出,会堆积:
- 显式调用
cache.Close()—— 它会停止清理 goroutine 并清空内部字段 - 生产环境建议复用 cache 实例,全局或按业务域初始化一次
- 如果必须动态生命周期,务必在对象销毁前调用
Close,比如在 struct 的Close()方法里
没调 Close 的 cache 即使被 GC,goroutine 仍可能运行数秒(取决于清理间隔),期间还持有对 cache 的引用,阻碍回收。
设置过期时间时,time.Duration 传 0 和负数有什么区别?
Set(key, value, 0) 表示“永不过期”,而 Set(key, value, -1) 或任意负值,会被 Go-Cache 当作“立即过期”,写入后下一次 Get 就找不到:
cache.Set("flag", true, time.Second*10) // 10秒后过期
cache.Set("config", cfg, 0) // 永不过期
cache.Set("temp", data, -time.Second) // 写入即失效
注意:0 和负数都绕过了过期检查逻辑,但语义相反。很多配置误用 -1 想表示“不设过期”,结果数据根本存不住。
真正麻烦的是混合使用永不过期和有时效的 key —— 清理 goroutine 仍会扫描全表,哪怕大部分 key 永久有效。如果确定某类数据绝对不淘汰,考虑拆成两个 cache 实例,一个带过期,一个不带。


















