参考网址:
https://tech.souyunku.com6844903760750198792#heading-0
https://tech.souyunku.com6844903813904596999#heading-3
https://tech.souyunku.com6844903986475057165#heading-6
https://tech.souyunku.com6844903651182542856
1. 问题描述
一般情况下,在Redis作缓存的系统中,请求到来时混先查看缓存中是否有数据,有则直接返回,没有这请求数据库并将数据写入Redis。
缓存穿透、缓存击穿和缓存雪崩,这三个问题均描述了在Redis作缓存时,大量请求绕过Redis直接打到数据库,导致数据库压力增大甚至不可服务的现象,但引发这三个问题的原因却各有区别。
2 缓存穿透
2.1 什么是缓存穿透?
问题简述:查询不存在的数据。(将缓存理解成一个一个网,查询原本就不能被网捕获的数据,理解为穿透)
缓存穿透是指大量请求都查询数据库中不存在的数据,缓存和数据都查询不到数据,这使得请求每次都打到数据库上。缓存穿透往往是来自于故意攻击。
示例
下图展示了查询id=-1的记录导致每次都查询数据库的现象。
2.2 解决方案
2.2.1 数据校验
可以对请求数据进行校验,比如对于id<0的直接返回错误,比如借助javax.validation包的@Valid、@NotNull等一系列注解。
2.2.2 缓存null值
将相应的key在redis中设置对应value为null(或者其它能反馈错误的值),这样请求到来时缓存一样可以生效。(此时可以将空对象的过期时间设置较短,否则攻击者请求大量数据库中不存在id,同样会缓存多个null值,也会带来redis使用内存剧增的问题)
2.2.3 布隆过滤器
3 缓存击穿
3.1 什么是缓存击穿
问题简述:查询一个失效的key。 (一个key过期,相当于网中破了一个洞,查询洞中过期的数据,理解为击穿)
缓存击穿是指大量请求都访问一个失效的key,使得请求都打到数据库上的现象。
3.2 解决方案
参考网址:
https://yq.aliyun.com/articles/290865
缓存击穿属于热点数据失效问题。
3.2.1 设置多级缓存
对热点数据设置多级缓存,比如设置本地缓存和二级缓存
3.2.2 使用互斥锁
对数据库某个记录的访问加互斥锁,只有第一个获取锁的线程可以请求数据库,然后将数据插入缓存,后面获取锁的线程查询缓存。
理解:
(1)可以先查询缓存,缓存没有数据加锁;
(2)加锁成功,重新查询缓存(类似于双重检查锁定,防止已有线程将数据写入缓存,直接从缓存读,节省访问数据库开销),没有则请求数据库,并将数据写入缓存;
(3)加锁失败(一般不会,假设获取锁的线程请求数据库时间很长),重试整个过程。
请求流程
互斥锁实现
互斥锁实现可参考:Redis实现分布式锁
3.2.3 热点散列
(暂未学习)
4 缓存雪崩
4.1 什么是缓存雪崩
问题简述:大量key在同一时间失效,或redis挂掉(雪崩很好理解,大量key同时失效) 缓存雪崩是指大量key在同一时间失效(Redis相当于不存在),或者redis挂掉,使得大量请求都打到数据库上的现象。缓存雪崩往往是由于程序使用定时任务删除redis key,或者是一次性对大量key设置相同过期时间。
示例
4.2 解决方案
4.2.1 大量key失效
(1)给key的过期时间加上随机值(一般可以设置为几十秒级别),这样可以大幅降低大量在同一时间过期的概率。
4.2.2 Redis宕机
(1)事发前:实现Redis高可用,比如哨兵、主从、cluster,降低redis宕机的概率。
(2)事发后:本地缓存+限流。
5. 总结
本篇文章介绍了三种Redis缓存无法正常生效,导致数据库崩溃的问题,分别介绍了三种问题相应的诱因和解决办法。限于本人只是水平,文章中如有任何错误,欢迎批评指正!