Java面试学习——redis篇(1):缓存三剑客
首要概念:Redis是服务端和数据库之间的中间层,位于本地缓存,,目的是为了减缓MySQL(磁盘)的数据查询压力,并且对服务端发起的请求快速响应已经存在缓存里的数据。
(我们可以理解为,数据库不能承受过多的查询压力,并且数据库的索引速度与能力会随着数据存储量的增多而成反比,亦或是我们为了让硬件方面的磁盘来足够应对请求的压力成本会非常高昂,天时地利人和之后我们自然会想要增加一个中间层——redis应运而生)
知道了上面的东西,那么我们要解决的第一个问题也很明了。
一. 缓存穿透
- 是什么?
- 查询一个不存在的数据,redis在DB里查询不到数据并且也不会直接写入缓存,就会导致每次请求都查询数据库;
- 为什么?
- 数据库请求路径泄露 / 数据库遭受恶意请求攻击(有点类似于DDoS);
- 怎么办?
- 查询的结果为空,但是都写入redis并且存入一个空数据,下次再请求就直接返回空数据(缺点是消耗内存,可能会发生数据不一致);
- 在redis处理查询请求之前部署布隆过滤器,确保被查询的内容存在才可进入缓存和数据库去查询(内存消耗较少,但是可能存在误判);
- 注意:布隆过滤器的实现依靠位图(bitmap)所以预热(加载热点数据)redis时也要预热布隆过滤器。
二.缓存击穿
- 是什么
- 一个刚刚过期的热点数据突然接受到了大量查询请求,而因为过期的缘故redis形同虚设,导致查询压力几乎全由数据库承受以至于数据库宕机;
- 为什么
- 数据库查询到结果后重新写入把数据存储到redis时间消耗过多(例如需要进行分组统计)而缓存重新存储数据期间大量的请求足够把数据库压垮;
- 怎么办
- 使用互斥锁,让查询读读不互斥但是读锁互斥,发现过期时不是立即load DB,先使用redis的SETnx设置一个互斥锁(相当于我们使得只有实际最先发起的请求去数据库查询)而当这个进程在进行过程中,所以直到这个数据写入完成之前,其余发起的请求都只能等待锁的释放,当操作成功返回后再进行load DB并释放锁,否则就重试get缓存方法。往后的请求就可以直接在redis里命中,达到了保护数据库的作用;
- 这个方法适合需要数据强一致性并且可以接受等待时间的业务使用,例如银行,但是性能较低
- 使用逻辑过期的方法,设置key的时候我们可以设置一个过期时间字段expire而不给key设置过期时间,当请求命中该数据并且通过该字段判断已过期时去获取互斥锁,开启另一个新线程进行缓存重建并,而原线程会返回一个过时的数据。在线程二进行缓冲重建期间,如果有第三个线程来发起请求并且获取锁失败了,那么它会放弃等待并且返回一个旧数据。互斥锁将在线程二里完成缓存重建后释放。
- 这个方法适合需要高可用、性能优秀的业务使用,例如车票系统,但是会存在一定的延时、不一致的情况
三.缓存雪崩
- 是什么
- 大片的热点数据彼此之间在极短时间内几乎是同时过期失效,导致查询请求在redis无法命中从而直接请求数据库进而压垮数据库
- 为什么
- 大量key设置的过期时间相近或相同导致过期时间也相同
- 怎么办
- 给不同key的TTL添加随机值;
- 利用redis集群提高服务的可用性;(哨兵模式,集群模式)
- 给缓存业务添加降级限流策略;(ngxin或spring cloud gateway)
- 该方法可以应对缓存三兄弟的问题,也是系统缓存的保底策略
- 给业务添加多级缓存;(Guava或Caffeine)

浙公网安备 33010602011771号