Kratos中不能只用Redis做缓存,因其数据层不内置缓存逻辑,需显式注入多级缓存(如本地bigcache/freecache+Redis)并自行实现穿透/击穿防护、失效通知与降级策略,单靠Redis远程调用延迟高且无自动兜底机制。

为什么 Kratos 里不能只用 redis 做缓存?
Kratos 是 Go 微服务框架,强调轻量、可插拔和面向接口设计。它的数据层(data 包)本身不内置缓存逻辑,所有缓存必须显式注入——这意味着你若只配一个 redis 客户端,就等于把全部压力压在远程调用上。实测中,单次 redis.Get 在局域网平均耗时 0.8–1.5ms;而本地内存查 Caffeine 或 sync.Map 是纳秒级。高频接口(如用户权限校验、商品类目树)每秒调用几十次,差的不是“快一点”,而是“能不能扛住”。更关键的是:Kratos 的 Repo 层天然支持多数据源组合,但没人帮你自动做缓存穿透、击穿、雪崩防护。
kratos/pkg/cache 不是万能的,别直接 import 就完事
Kratos 官方 kratos/pkg/cache 提供了 Cache 接口和几个基础实现(如 memcache、redis),但它只是“缓存能力抽象”,不是“缓存策略实现”。它不处理:
• 多级 fallback(本地没命中 → redis → db)
• 缓存更新时机(写 DB 后要不要删 redis?删不删本地?删哪个 key?)
• 热点 key 防护(比如某个 category:1024 被疯狂请求,本地缓存未命中就会打穿 redis)
所以实际项目中,你要自己封装一层 MultiLevelCacheRepo,把 cache.Local 和 cache.Redis 拼起来,再套上 cache.Batcher 或 cache.LoadingCache 行为。
本地缓存选 gocache 还是 Caffeine?Go 里其实该用 bigcache 或 freecache
Go 生态没有 Java 那种成熟的 Caffeine 等价物。官方 sync.Map 并发安全但无淘汰策略;lru 库线程安全但 GC 压力大。生产推荐:
• bigcache:基于分片 + 时间戳淘汰,适合高并发读、低频写场景(如字典表、配置项)
• freecache:提供类似 LevelDB 的内存管理,支持 TTL 和近似 LRU,比 bigcache 多 10% 内存但更准
• 别用 gocache:它底层用 map + mutex,QPS 上千后锁竞争明显,我们在线上压测过,TPS 卡在 4k 左右就上不去
示例初始化:
cache := freecache.NewCache(100 * 1024 * 1024) // 100MB<br>repo := &UserRepo{<br> local: cache,<br> redis: redisClient,<br> db: sqlDB,<br>}
缓存穿透/击穿怎么防?Kratos 里最有效的其实是 cache.Once + redis.SetNX
缓存穿透(查不存在的 ID)和击穿(热点 key 过期瞬间大量请求打穿)在 Kratos 中不能靠“加锁重试”硬扛,Go 的 goroutine 泄漏风险太高。我们线上通用解法是两层防护:
• 一级:本地 cache.Once(用 sync.Once 封装),确保同一时刻只有一个 goroutine 去加载数据
• 二级:redis 层用 SET key value EX 60 NX 设置互斥锁,避免多个实例同时回源
关键点:
• Once 必须按 key 分片(比如用 key % 64 取模),否则全量请求会串行化
• redis 锁超时时间必须短于业务数据 TTL,否则锁残留导致后续请求永远等不到
• 回源失败时,本地要缓存空对象(nil + 短 TTL),防止反复穿透
多级缓存不是堆组件,是定策略。Kratos 的干净架构反而逼你直面问题:本地缓存失效怎么通知其他实例?Redis 宕机时本地是否允许 stale 数据?这些细节没想清楚,加再多级也白搭。











