选go-cache还是MemVault取决于场景:仅需轻量级进程内缓存选go-cache;需持久化、批量操作或后台清理选MemVault;LevelDB类方案不适用于轻量本地缓存。

选 go-cache 还是 MemVault?看场景再决定
如果你只需要进程内缓存、支持 TTL、不关心持久化,go-cache 是最轻量的选择:零依赖、开箱即用、API 就三个方法(Set/Get/Delete)。它底层就是带读写锁的 map[string]interface{},启动快、无磁盘 IO、适合会话临时存储或配置快照。
但如果你需要数据落地(哪怕只是冷启自动加载 JSON 文件)、支持批量操作、或者希望 key 过期后能被后台 goroutine 清理而非靠 Get 时惰性判断,MemVault 更合适——它把 TTL、持久化、内存回收都封装进一个结构体里,API 稍多但更贴近生产需求。
LevelDB 类方案(如 goleveldb)不适合“轻量级本地”定位:它要文件路径、权限、目录预建,还涉及序列化/反序列化开销。除非你明确需要磁盘持久 + 高并发写吞吐,否则别一上来就引入。
用 go-cache 封装时最容易漏掉的三件事
go-cache 看似简单,但直接 new 出来就用,上线后常出问题:
立即学习“go语言免费学习笔记(深入)”;
- 没设默认过期时间:
cache := cache.New(5*time.Minute, 10*time.Minute)两个参数分别是「清理间隔」和「默认过期时长」,第二个漏设会导致所有 key 永不过期 - 没处理 Get 返回的 bool:
val, ok := cache.Get("key"),忽略ok直接用val可能 panic(比如值是nil且类型断言失败) - 没限制 size 或没配 GC 触发阈值:它不自动限容,大量写入后内存只增不减;建议搭配
cache.SetDefaultExpiration+ 定期调cache.DeleteExpired()
MemVault 初始化必须绕开的坑
MemVault 默认启用持久化,但它的 Open 方法对路径很敏感:
- 路径不能是相对路径:
"./data/cache"在容器里大概率找不到,必须用绝对路径或从os.Getenv("CACHE_DIR")读取 - 目录权限要提前检查:
os.Stat跟着os.MkdirAll一起用,否则首次写入时因权限拒绝静默失败 - 恢复逻辑默认开启,但如果 JSON 文件损坏,
Open会返回 error 并清空内存——这不是 bug,是设计行为,得在 wrapper 里加校验或 fallback 日志
示例初始化片段:
db, err := memvault.Open(os.Getenv("CACHE_PATH"), &memvault.Options{
AutoSaveInterval: 30 * time.Second,
MaxSize: 10000,
})
if err != nil {
log.Fatal("failed to open memvault:", err)
}要不要自己封装一层?取决于你是否需要统一错误语义
直接暴露 go-cache 或 MemVault 的原生 API,调用方就得处理各种 error 类型(cache.KeyNotFoundError、memvault.ErrKeyNotFound、json.UnmarshalError),分散且难 mock。
推荐加一层薄 wrapper,只做三件事:
- 统一返回
error类型(比如自定义CacheMissError) - 把
interface{}强制转成具体类型(GetInt64("counter")、GetStringSlice("tags")) - 把 TTL 参数标准化(统一用秒或
time.Duration,不混用字符串或 int)
这个 wrapper 不需要抽象接口,就一个 struct 带字段 cache *cache.Cache 或 db *memvault.DB,方法名保持 Get/Put/Delete 即可。复杂度控制在 100 行以内,维护成本远低于换库。
真正容易被忽略的是:无论选哪个库,**TTL 的精度都不等于定时器精度**——go-cache 依赖清理 goroutine 间隔扫描,MemVault 用最小堆+后台 ticker,两者都无法保证 key 到点立刻消失。如果业务强依赖精确过期(比如 token 严格 30 分钟失效),得额外加一层外部校验。


















