Redis缓存三兄弟:穿透、击穿、雪崩全解析
缓存三兄弟
1. 缓存穿透
定义:查询一个不存在的数据,缓存中没有,数据库中也没有,导致每次请求都直接打到数据库。
危害:恶意攻击者可以利用这个漏洞,大量请求不存在的数据,导致数据库压力过大。
解决方案:
- 缓存空对象:将空结果也缓存起来,但要设置较短的过期时间,避免数据不一致
- 布隆过滤器:在缓存之前增加一层布隆过滤器,快速判断数据是否可能存在
- 原理:通过多个哈希函数将数据映射到位数组中,查询时判断对应位置是否都为1
- 特点:可能存在误判(说存在但实际不存在),但不会漏判(说不存在就一定不存在)
- 实现:可使用Redisson或Guava的布隆过滤器
2. 缓存击穿
定义:某个热点key过期时,大量并发请求同时访问这个key,导致请求直接打到数据库。
解决方案:
-
互斥锁:
- 优点:强一致性,确保只有一个线程查询数据库
- 缺点:性能较差,其他线程需要等待
-
逻辑过期:
- 实现:不设置Redis过期时间,在value中存储逻辑过期时间
- 优点:高可用性,性能优秀
- 缺点:可能会返回过期数据,最终一致性
3. 缓存雪崩
定义:大量缓存key在同一时间失效,或Redis服务宕机,导致大量请求直接打到数据库。
解决方案:
- 随机过期时间:给不同key的TTL添加随机值,避免同时过期
- Redis集群:提高服务可用性,避免单点故障
- 限流降级:在缓存大量失效时,对请求进行限流和降级处理
- 多级缓存:建立多层缓存架构,如本地缓存+Redis+数据库
缓存一致性问题
双写一致性
定义:当修改数据库数据时,需要同时更新缓存数据,保持缓存和数据库数据一致。
解决方案
1. 异步通知方案(适用于允许延时一致的业务)
基于MQ消息队列:
- 更新数据库后,发送MQ消息通知缓存删除
- 优点:解耦、可靠性高
- 缺点:有延时,可能出现短暂的数据不一致
基于Canal的异步通知:
- Canal伪装成MySQL的从节点,通过读取binlog来更新缓存
- 优点:无需修改业务代码,对业务无侵入
- 缺点:依赖binlog,有一定的技术复杂度
2. 强一致性方案
Redisson读写锁:
- 共享锁(读锁):多个线程可以同时获取读锁,进行读操作
- 排他锁(写锁):只有一个线程可以获取写锁,阻塞其他线程的读写操作
读操作流程:
- 尝试获取读锁
- 缓存命中直接返回
- 缓存未命中查询数据库
- 将结果写入缓存并设置过期时间
- 释放读锁
写操作流程:
- 获取写锁
- 先删除缓存
- 更新数据库
- 延迟删除缓存(延迟双删)
- 释放写锁
3. 延迟双删策略
为什么需要延迟双删:
- 先删缓存再更新数据库:并发读请求可能在数据库更新前查询到旧数据并写入缓存
- 先更新数据库再删缓存:并发读请求可能在缓存删除前查询到旧数据
延迟双删流程:
- 删除缓存
- 更新数据库
- 延迟N秒后再次删除缓存(N通常为读操作的耗时)
Redis持久化
RDB(Redis Database)
- 原理:在指定时间间隔内将内存中的数据集快照写入磁盘
- 优点:文件紧凑,恢复速度快,对性能影响小
- 缺点:可能丢失两次快照间的数据
AOF(Append Only File)
- 原理:记录每个写操作,重启时重新执行这些命令来恢复数据
- 优点:数据安全性高,可以做到秒级数据丢失
- 缺点:文件体积大,恢复速度慢
建议:生产环境同时开启RDB和AOF,确保数据安全性。
面试要点总结
- 缓存三兄弟:穿透、击穿、雪崩的区别和解决方案要熟练掌握
- 一致性策略:根据业务需求选择合适的一致性方案
- 读写锁:理解共享锁和排他锁的区别和应用场景
- 延迟双删:理解为什么需要延迟双删,以及如何确定延迟时间
- 持久化:了解RDB和AOF的特点和适用场景
记住:没有完美的解决方案,只有最适合当前业务场景的方案。面试时要结合具体业务场景来回答问题。