缓存策略需按接口语义分层:配置类等高频读低频写接口适合Redis缓存,余额订单等强一致性接口应避免;须手动控制读写、只缓存业务数据、key带版本号、过期时间依SLA设定、配合主动失效;Redis客户端初始化必须显式传含Database的Config;缓存操作需原子化;推荐本地缓存+Redis多级架构,并防范缓存穿透。

缓存策略得按接口语义分层,不能全塞进Redis
不是所有接口都适合用 Redis 缓存。高频读、低频写、数据变更不敏感的接口(如配置项、地区列表、商品类目)才值得缓存;而用户余额、订单状态这类强一致性要求的,缓存反而引入脏读风险。Iris 本身不提供自动缓存装饰器(比如 @cache),必须手动在 handler 里控制读写逻辑。
常见错误是把整个 HTTP 响应体序列化后往 Redis 一存了事——这会导致 Content-Type、Header、状态码丢失,后续没法直接复用响应。正确做法是只缓存业务数据结构(如 []Product),由 handler 自己组装响应。
- 缓存 key 必须带版本号或哈希前缀,例如
api:products:v2:category=shoes,避免字段结构变更后旧缓存污染新逻辑 - 过期时间不能拍脑袋设 1 小时,要结合业务 SLA:配置类可设 24h,促销活动页建议 5~10 分钟
- 不要依赖 Redis 的 EXPIRE 自动清理,关键接口需配合主动失效(如更新商品时发消息清对应 key)
Redis 客户端初始化必须显式传 Config,别信默认值
Iris 的 iris.Redis() 不会读取环境变量或配置文件自动连接,必须手动构造 redis.Config 并传入。漏掉 Database 字段是最常见坑:留空或传字符串 "1" 都会被 Go-Redis 底层静默忽略,最终连到 DB 0 —— 导致缓存和 session 数据混在一起,删库跑路式误操作。
示例中必须写清楚:
redisConf := redis.Config{
Network: "tcp",
Addr: os.Getenv("REDIS_ADDR"),
Password: os.Getenv("REDIS_PASSWORD"),
Database: 1, // 注意:是 int 类型,不是 string
Prefix: "cache:",
}
另外 Prefix 要统一,否则不同服务写入的 key 无法被同一套清理逻辑覆盖。
缓存读写必须原子,别用 GET + SET 组合
并发请求同时查缓存未命中,再各自写入,就会造成缓存击穿+重复计算。正确方式是用 Redis 的 SET key value EX 300 NX 原子命令,靠返回值判断是否真正写入成功。
Iris 没有封装好的 CacheGetOrSet 函数,得自己封装:
- 先尝试
redisClient.Set(ctx, cacheKey, data, ttl).Val(),若返回"OK",说明是首次写入,执行业务逻辑并写回 - 若报错
redis.Nil,说明 key 已存在,直接redisClient.Get(ctx, cacheKey).Result()取值 - 务必检查
ctx.Err(),防止 handler 超时后还继续写缓存
本地缓存 + Redis 多级组合才是真实场景
纯 Redis 缓存扛不住突发流量,尤其当后端 DB 已经慢成瓶颈时,Redis 查询也会排队。iris-admin 实践中常用 bigcache 或 freecache 做一级本地缓存(内存中),Redis 做二级共享缓存。
关键点在于失效同步:
- 本地缓存不支持主动失效,只能设短 TTL(如 10 秒),靠时间自然过期
- Redis 缓存更新时,必须广播一个「失效事件」(如 Pub/Sub 发
cache:invalidate:products),所有实例监听并清空本地对应条目 - 别用全局 map 存本地缓存,Go runtime GC 对大 map 效率差,要用专为缓存设计的库
最易被忽略的是缓存穿透:恶意请求大量查不存在的 ID(如 /user/999999999),导致每次都要穿透到 DB。必须对空结果也缓存(如存 null 字符串,TTL 缩短至 60 秒),并在 key 设计上加布隆过滤器前置校验——这个环节 Iris 不参与,得你亲手加。

















