go-cache是单机缓存工具而非必学库,适合已掌握map、sync.RWMutex等基础后快速落地线程安全带过期的本地存储;选cache.New或NewSharded取决于写压力:读多写少用前者,写超200 QPS必须用后者分片。

go-cache 不是学习 Go 语言的“必学库”,而是解决单机缓存具体问题的工具。它适合在你已经理解 map、sync.RWMutex、time.Duration 和 goroutine 基础后,用来快速落地一个带过期、线程安全的本地存储——不是为了学它本身,而是用它绕过手写并发 map 的坑。
cache.New 和 cache.NewSharded 怎么选?看写压力,不是看教程
刚起步写 demo 或配置加载,用 cache.New 完全够用;但一旦模拟用户登录态刷新、计数器累加这类操作,每秒写入超 200 次,cache.New 就会卡在 sync.RWMutex 上,CPU 升高但吞吐不涨。
- 读多写少(如地区字典、静态配置)→
cache.New(5*time.Minute, 10*time.Minute) - 写密集(如 session 更新、实时指标)→ 必须用
cache.NewSharded(64, 5*time.Minute, 10*time.Minute),分片数 32–64 足够,别盲目设 128 -
cache.NewSharded的哈希算法是djb33,不依赖hash/fnv,快且分布均匀,不用自己实现
Set 时传 0 或不传 duration 会怎样?
传 0 ≠ 永不过期,而是“立即过期”。cache.DefaultExpiration 是常量(5 分钟),不是默认值——它只在你显式传入时生效;没传就 panic。
- 想永不过期 → 必须传
cache.NoExpiration,不能传0或省略 - 想设 30 秒 → 显式写
c.Set("key", val, 30*time.Second),别依赖构造时的 “默认” - 后台清理协程每
10*time.Minute扫一次,所以过期项最多延迟这么久才真正从内存移除
Get 返回 interface{},为什么不能直接断言?
c.Get("x") 返回 (interface{}, bool),漏掉 bool 判断会导致两种静默错误:key 不存在时返回 nil,强制 .(*User) 直接 panic;或者用 == nil 判断,因 interface{} 的 nil 和指针 nil 不等价而失效。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 正确写法:
if v, found := c.Get("user:123"); found { user := v.(*User) } - 结构体建议存指针:
c.Set("user:123", &u, ttl),避免值拷贝开销 -
cache.Items()返回的是 map 快照,不是实时状态,不能用来判断 key 是否“一定存在”
GetOrSet 类逻辑怎么安全实现?
go-cache 没有原子的 GetOrSet,if !found { heavyLoad(); Set() } 在并发下会重复执行 heavyLoad()。
立即学习“go语言免费学习笔记(深入)”;
- 简单场景:用
sync.Once+ 全局变量或指针缓存初始化结果 - 高频场景:改用
sync.Map配合atomic标记生成状态,比嵌套锁更轻量 - 坚持用
go-cache?加一层sync.Mutex控制 key 粒度,但要注意锁粒度和性能平衡
cache.NoExpiration 的项仍会被 GC 回收,也会在 c.Flush() 或进程重启时消失——如果你把它当配置中心用,上线第一天就会踩坑。

















