go-cache的set()返回false表示key已存在且更新成功,并非失败;需首次写入用add();高qps(>200)应改用newsharded分片缓存,避免rwmutex串行瓶颈。

go-cache 的 Set() 返回 false 不是失败,是 key 已存在
很多人看到 cache.Set("user:123", u, 5*time.Minute) 返回 false 就以为写入失败,立刻打日志甚至重试——这会刷爆日志且毫无意义。它只表示该 key 已存在,value 已被更新,操作本身成功。
- 需要“首次插入”语义时,改用
Add():它只在 key 不存在时写入,失败才返回false - 想判断是否真出错?目前
go-cache没暴露 error 路径,扩容失败极罕见,也不抛异常 - 别用
if !cache.Set(...)做错误处理,那是逻辑误判
写入 QPS > 200 时,必须用 cache.NewSharded() 替代 cache.New()
cache.New() 底层是单个 sync.RWMutex,所有写操作排队串行。实测写入超 200 QPS 后,CPU 占用飙升但吞吐卡住,锁成为瓶颈。
- 推荐分片数:
cache.NewSharded(64, 5*time.Minute, 10*time.Minute) - QPS > 10k 场景可设为 512,但超过 128 分片后收益递减,内存开销明显上升
- 读远多于写(如配置缓存)时,
cache.New()反而更稳——读不加锁,无哈希开销
别把 sync.Map 当通用缓存,它不支持 TTL 且写多会内存泄漏
sync.Map 看起来省事,但它是为“读多写少 + 键数量稳定”设计的。往里塞上万条数据、频繁 Store() 和 Delete(),会导致 dirty map 不断复制,内存持续上涨,GC 压力陡增。
- 适合场景:缓存几十个静态配置项、连接池元信息、用户会话 ID 映射(数量可控且生命周期长)
- 完全不支持过期逻辑,要 TTL 必须自己包一层时间戳字段 + 定期扫描,成本高于直接用
go-cache - 别在 HTTP handler 里每次新建
sync.Map实例,缓存完全失效
bigcache 的 “零 GC” 有前提,value 频繁变更时反而比 go-cache 慢
bigcache 把 value 序列化成 []byte 存进大 buffer,避免为每个 value 分配堆内存,从而减少 GC 压力。但它要求 value 必须可序列化,且每次 Set() 都要重新编码 + 拷贝。
- 适合:value 小(
- 不适合:value 是结构体且高频更新(如实时计数器),此时
go-cache存指针更快 - 初始化必须显式传
shards,默认 256 对中高并发偏小;key必须是string,不能是 struct 或 []byte
Items() 还是后台清理逻辑,都只是快照或惰性扫描。Get 后必须用返回的 found bool 判断,再做类型断言,否则 panic 或静默错误几乎必然发生。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











