go-cache不提供抽象失效策略,仅支持ttl+定时清理;cache.new()传0或负数会panic,defaultexpiration须用cache.noexpiration,cleanupinterval必须>0。

go-cache 本身不提供“失效策略”这种抽象能力,它只提供 TTL + 定时清理的被动机制;所谓“失效”,必须由你主动控制时机、范围和方式。
cache.New() 初始化失败就 panic,怎么避开
常见错误是传 0 或负数给 cache.New() 的两个参数。第一个是默认过期时间(defaultExpiration),第二个是清理间隔(cleanupInterval)。传 0 不代表“永不过期”,而是触发内部 time.Duration 转换失败,导致 nil 指针解引用 panic。
-
defaultExpiration设为永不过期,必须用cache.NoExpiration,不能用0 -
cleanupInterval必须 >0;设为0不仅不启动清理 goroutine,还会 panic - 正确写法示例:
cache.New(5*time.Minute, 1*time.Minute)或cache.New(cache.NoExpiration, 30*time.Second) -
cache.DefaultExpiration是常量0,仅用于Set()第三个参数,表示“用实例默认值”,不可传给New()
Set() 返回 false 就以为写入失败?这是误解
cache.Set() 返回 bool 表示“是否首次插入该 key”,不是操作成败。它几乎不会失败(内部 map 扩容失败极罕见,且不暴露 error)。
- key 已存在 → 更新 value 并返回
false - key 不存在 → 插入并返回
true - 误用
if !cache.Set(k, v, exp) { log.Println("set failed") }会导致日志刷屏 - 需要“首次设置”语义,请改用
cache.Add(key, value, exp),它只在 key 不存在时写入,失败才返回false
缓存“失效”不是靠等过期,而是靠你主动删
go-cache 没有主动失效接口,也没有基于访问频次或容量的淘汰逻辑。TTL 只决定“何时可删”,DeleteExpired() 只删已过期项,但不解决内存膨胀;后台 janitor 默认每 cleanupInterval 扫描一次全部 items,时间复杂度 O(n),大缓存下开销明显。
- 数据变更后,必须显式调用
cache.Delete("key")或cache.DeleteRegex("user:.*")(需启用 regex 支持) - 别用
cache.Set(key, nil, ...)试图“清空”,它只是存了个nil,key 还在 - 批量失效建议按前缀组织 key,如
"user:123:profile"、"user:123:orders",便于用正则或字符串匹配批量清理 - 如果要实现“更新 DB 后删缓存”,顺序必须是:DB 写成功 →
cache.Delete(...)→ 记录日志(删失败也不该回滚 DB)
Get() 返回 (nil, false) 却刚 Set 过,怎么回事
这不是并发竞争问题,而是语义误读或数据类型陷阱。go-cache 的 Get() 返回 (interface{}, bool),第二个布尔值才表示“是否命中有效条目”,nil 值本身不能用来判断存在性。
- 存了
nil *User,Get()会返回(nil, true),不是未命中 - 存了
map/slice/chan等引用类型,后续修改原变量会“意外”改掉缓存内容(因共享底层数据) - 并发写入时没注意:
Set传的是指针,多个 goroutine 共享底层数据,Get出来看似一致,实际是同一块内存 - 正确写法永远是:
if val, found := cache.Get("key"); found { /* 处理 val */ } else { /* 真未命中,该查 DB */ }
真正难处理的从来不是“怎么存”,而是“什么时候删、删哪些、删完怎么降级”。go-cache 把这部分交给你手动兜底 —— 它不帮你做决策,只提供基础容器。如果你需要自动驱逐、成本控制或强一致性,该换库了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











