Redis 缓存设计与常见问题处理指南
wxk1991 Lv5

Redis 缓存设计与常见问题处理指南

Redis 常被用来提升接口响应速度、降低数据库压力。但缓存不是简单地 get 一下、set 一下。过期时间、数据一致性、缓存穿透、缓存击穿、缓存雪崩都会影响线上稳定性。

本文从后端业务角度总结 Redis 缓存设计的常见模式和风险处理方法。


一、缓存适合什么数据

适合缓存的数据通常有几个特点:

  • 读取频率高
  • 写入频率低或可接受短暂延迟
  • 计算成本高
  • 数据体积可控
  • 对强一致性要求不高

典型例子:

1
2
3
4
5
6
用户基础信息
商品详情
系统配置
排行榜
热门文章列表
接口鉴权结果

不适合缓存的数据:

1
2
3
4
余额扣减
库存最终扣减
强一致订单状态
高频变化的大对象

这些数据可以使用 Redis 辅助,但不能只靠普通缓存逻辑保证正确性。


二、Cache Aside 模式

最常见的模式是 Cache Aside:

1
2
3
4
5
先读缓存
缓存命中则返回
缓存未命中则查数据库
数据库查到后写入缓存
返回结果

伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func GetUser(ctx context.Context, id int64) (*User, error) {
key := fmt.Sprintf("user:%d", id)

cached, err := redis.Get(ctx, key).Result()
if err == nil {
return DecodeUser(cached)
}

user, err := db.FindUser(ctx, id)
if err != nil {
return nil, err
}

redis.Set(ctx, key, EncodeUser(user), 10*time.Minute)
return user, nil
}

这个模式简单可靠,适合大多数读多写少场景。


三、缓存 Key 设计

Key 要稳定、清晰、可定位:

1
2
3
4
user:1001
article:detail:9527
config:site
rank:daily:20260604

建议:

  • 使用业务前缀区分模块
  • 包含必要版本或日期
  • 避免过长 key
  • 不要把用户输入原样拼进 key

如果接口返回结构升级,可以加版本:

1
user:v2:1001

这样旧缓存不会污染新结构。


四、过期时间设置

不要所有 key 都设置同一个过期时间。可以按数据类型设计:

1
2
3
4
5
用户信息       5 到 30 分钟
系统配置 1 到 10 分钟
热门列表 30 秒到 5 分钟
排行榜 按业务周期过期
临时验证码 1 到 10 分钟

为了避免大量 key 同时过期,可以增加随机抖动:

1
ttl := 10*time.Minute + time.Duration(rand.Intn(60))*time.Second

这能降低缓存雪崩风险。


五、缓存穿透

缓存穿透指查询一个不存在的数据,每次缓存都 miss,然后打到数据库。

常见处理方式:

1. 缓存空值

数据库查不到时,缓存一个短 TTL 的空值:

1
2
user:999999 = "__NULL__"
TTL = 60 秒

下次再查同一个不存在 ID,就不会直接打数据库。

2. 参数校验

明显非法的 ID、页码、类型,应该在进入数据库前拦截。

3. 布隆过滤器

当不存在查询非常多时,可以使用布隆过滤器提前判断 ID 是否可能存在。


六、缓存击穿

缓存击穿指某个热点 key 过期瞬间,大量请求同时打到数据库。

处理方式:

  • 热点 key 设置较长 TTL
  • 后台定时刷新热点缓存
  • 缓存 miss 时加互斥锁

互斥思路:

1
2
第一个请求获得锁,负责查数据库并重建缓存
其他请求短暂等待或返回旧值

不要让所有请求同时重建同一个热点 key。


七、缓存雪崩

缓存雪崩指大量 key 同时失效,数据库瞬间承压。

常见原因:

  • 批量导入时设置了相同 TTL
  • Redis 实例故障
  • 定时任务集中刷新失败

处理方式:

  • TTL 增加随机抖动
  • 热点数据分批刷新
  • 关键接口增加限流
  • Redis 做高可用部署
  • 数据库侧保留保护策略

八、写入后如何更新缓存

常见策略是“先更新数据库,再删除缓存”:

1
2
3
更新数据库
删除对应缓存 key
下次读取时重建缓存

为什么不是直接更新缓存?因为复杂对象可能有多个缓存视图,例如:

1
2
3
user:1001
user:list:active
team:8:members

直接更新所有相关缓存容易漏。删除缓存让下一次读取重建,通常更简单。

如果对一致性要求更高,可以结合消息队列、binlog 订阅或延迟双删。


九、监控指标

Redis 缓存上线后要关注:

  • 命中率
  • QPS
  • 慢查询
  • 内存使用
  • key 数量
  • 过期 key 数量
  • 连接数
  • 淘汰策略触发次数

如果命中率很低,说明缓存设计可能没有减少数据库压力,反而增加了系统复杂度。


十、实践建议

缓存设计要明确回答三个问题:

1
2
3
缓存什么数据?
允许多久不一致?
缓存失效时系统能否扛住?

Redis 很快,但它不是数据库正确性的替代品。缓存的核心价值是提升读取效率和削峰,业务正确性仍然要由数据库约束、事务和清晰的数据模型来保证。