Redis 缓存学习笔记:商品详情缓存和常见问题
写在前面
Redis 是 Java 后端学习中绕不开的一块。刚开始我对 Redis 的理解很简单:查数据库慢,就把结果放进缓存。
后来做项目和看业务代码时,才发现缓存不是“加上 Redis 就完事”。缓存 key 怎么设计、过期时间怎么定、数据不一致怎么办、缓存穿透和击穿怎么处理,这些都需要结合具体业务判断。
这篇文章是我对商品详情缓存场景的学习整理,不包装成生产经验,只记录我目前能理解和讲清楚的部分。
为什么商品详情适合缓存
商品详情、文章详情、用户基础信息这类接口有一个共同特点:
- 读多写少
- 数据结构相对固定
- 页面访问频率比较高
- 可以接受短时间缓存
所以这类数据比较适合先查 Redis,缓存没有命中再查 MySQL。
大致流程是:
1 | |
基础代码思路
1 | |
这段代码能跑,但还不够完善。真正需要考虑的是缓存异常情况。
缓存穿透
缓存穿透指的是查询一个数据库里也不存在的数据。比如有人一直请求不存在的商品 ID,每次缓存查不到,又继续打到数据库。
常见处理方式:
- 参数校验,明显非法的 ID 直接拦截
- 缓存空值,但过期时间设置短一点
- 使用布隆过滤器提前判断 ID 是否可能存在
我在项目练习中更常用的是缓存空值:
1 | |
这样可以减少重复请求不存在数据时对数据库的压力。
缓存击穿
缓存击穿通常发生在热点 key 失效的一瞬间,很多请求同时查不到缓存,然后一起访问数据库。
可以考虑:
- 热点数据过期时间设置得更长
- 提前预热热点数据
- 查询数据库时加互斥锁
- 使用逻辑过期方案
我目前更能理解的是互斥锁方案:缓存没命中时,先尝试拿锁,拿到锁的请求去查数据库并回写缓存,其他请求短暂等待或稍后重试。
1 | |
这里也要注意:锁一定要有过期时间,避免异常情况下锁无法释放。
缓存雪崩
缓存雪崩是大量 key 在同一时间失效,导致请求集中打到数据库。
比较简单的做法是给过期时间加随机值:
1 | |
这样不同 key 的过期时间会错开一些。
缓存一致性
缓存一致性是我觉得比较难的一块。更新数据库后,缓存应该怎么处理?
常见做法是:
- 先更新数据库,再删除缓存
- 不直接更新缓存,避免写入旧值
- 对一致性要求高的场景,不要完全依赖缓存
例如商品信息更新后:
1 | |
下一次查询时重新从数据库读取并写入缓存。
这个方案不是绝对完美,但对于很多读多写少的业务已经够用。真正强一致的场景,需要结合事务、消息队列或延迟双删等方案继续设计。
我现在对 Redis 的理解
Redis 的价值不是单纯“让接口快”,而是用内存缓存减少数据库重复查询。但用了 Redis 之后,也会引入新的复杂度:
- 数据是否一致
- key 是否合理
- 过期时间是否合适
- 异常场景是否兜底
- 缓存失效时数据库能不能扛住
所以我现在写简历或博客时,不会夸大自己做了多复杂的缓存架构,而是更愿意把具体问题讲清楚。
小结
这篇文章主要整理了 Redis 在商品详情缓存场景下的几个基础问题:穿透、击穿、雪崩和一致性。
对我来说,Redis 不是一个“加分词”,而是一个需要结合业务认真设计的工具。能讲清楚为什么缓存、缓存什么、什么时候失效、失败后怎么兜底,比写“高并发架构”更真实。