黑马面试八股文之redis穿透,击穿,雪崩

前言:

Rdeis几乎是面试必考的内容,而穿透缓存雪崩三兄弟无疑是重点中的重点,本文将帮助你快速梳理里面的知识点,还有面试题巩固~
我们先来一首打油诗热热场:
《缓存三兄弟》
穿透无中生有key,布隆过滤null隔离。
缓存击穿过期key, 锁与非期解难题。
雪崩大量过期key,过期时间要随机。
面试必考三兄弟,可用限流来保底。

一、缓存穿透:

例:
一个get请求:api/news/getById/1

·什么是缓存穿透:

如果别人知道了我的请求路径,恶意查询一个不存在的数据,mysql查询不到数据也不会直接写入缓存,就会导致每次请求都查数据库。导致数据库被击垮或宕机。

·解决方案一:

缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存
{key:1,value:null}
··优点:简单
··缺点:消耗内存,可能会发生不一致的问题

·解决方案二:

在缓存之前加一层布隆过滤器,没有什么是加一层解决不了滴~
在这里插入图片描述

布隆过滤器:
bitmap(位图):相当于是一个以(bit)位为单位的数组,数组中每个单元只能存储二进制数0或1
布隆过滤器作用:布隆过滤器可以用于检索一个元素是否在一个集合中。

在这里插入图片描述
在这里插入图片描述

··优点:内存占用较少,没有多余key
··缺点:实现复杂,存在误判

问:你能介绍一下布隆过滤器吗?
候选人:嗯,是这样的。布隆过滤器主要是用于检索一个元素是否在一个集合中。我们当时使用的是Redisson实现的布隆过滤器。它的底层原理是,先初始化一个比较大的数组,里面存放的是二进制0或1。一开始都是0,当一个key来了之后,经过3次hash计算,模数组长度找到数据的下标,然后把数组中原来的0改为1。这样,三个数组的位置就能标明一个key的存在。查找的过程也是一样的。当然,布隆过滤器有可能会产生一定的误判,我们一般可以设置这个误判率,大概不会超过5%。其实这个误判是必然存在的,要不就得增加数组的长度。5%以内的误判率一般的项目也能接受,不至于高并发下压倒数据库。

二、缓存击穿:

·什么是缓存击穿:

	给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮。

解决方案一:互斥锁(强一致,但性能差)

在这里插入图片描述

解决方案二:逻辑过期(高可用,但性能优)

在这里插入图片描述

逻辑过期就是加一个超时时间字段,实现“逻辑过期”
在这里插入图片描述

问:什么是缓存击穿?怎么解决?
候选人:嗯!缓存击穿的意思是,对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这个时间点对这个Key有大量的并发请求过来。这些请求发现缓存过期,一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。解决方案有两种方式:
第一种方案,可以使用互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的 SETNX 去设置一个互斥锁。当操作成功返回时,再进行 load db的操作并回设缓存,否则重试get缓存的方法。
第二种方案是设置当前key逻辑过期,大概思路如下:

  1. 在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间;
  2. 当查询的时候,从redis取出数据后判断时间是否过期;3) 如果过期,则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据可能不是最新的。当然,两种方案各有利弊:如果选择数据的强一致性,建议使用分布式锁的方案,但性能上可能没那么高,且有可能产生死锁的问题。如果选择key的逻辑删除,则优先考虑高可用性,性能比较高,但数据同步这块做不到强一致。

三、缓存雪崩:

·什么是缓存雪崩:

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:

1.给不同的Key的TTL添加随机值
2.利用Redis集群提高服务的可用性
(哨兵模式、集群模式)
3.给缓存业务添加降级限流策略
(ngxin或spring cloud gateway)
4.给业务添加多级缓存
(Guava或Caffeine)

问: 什么是缓存雪崩?怎么解决?
候选人:嗯!缓存雪崩意思是,设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重而雪崩。与缓存击穿的区别是:雪崩是很多key,而击穿是某一个key缓存。解决方案主要是,可以将缓存失效时间分散开。比如,可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机。这样,每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件

在Java与Redis的应用场景中,缓存击穿、缓存穿透、缓存雪崩是影响系统性能与稳定性的重要问题,下面分别介绍它们的原理及解决方案。 ### 缓存穿透 原理:指客户端请求的数据在缓存和数据库中都不存在,这样每次请求都会穿透缓存,直接访问数据库,当大量此类请求到来时,会对数据库造成巨大压力。 解决方案: - 缓存空对象:当数据库查询结果为空时,将空对象存入缓存,下次相同请求直接从缓存获取空对象,避免再次查询数据库。 - 布隆过滤器:在请求到达缓存之前,使用布隆过滤器判断请求的数据是否可能存在。如果布隆过滤器判定数据不存在,则直接返回,避免对缓存和数据库的无效访问。 - id格式校验:对请求的id进行格式校验,过滤掉明显非法的请求,减少无效请求对系统的影响 [^1][^2][^4]。 ### 缓存击穿 原理:部分热门key过期时,大量请求同时访问该key,由于缓存中该key已失效,这些请求会直接穿透到数据库,给数据库带来巨大压力。 解决方案: - 使用锁:对查询数据库的操作加锁,同一时间只有一个请求可以查询数据库,其他请求等待,查询到数据后存入缓存,后续请求可从缓存获取数据。 - 逻辑过期方案:不设置缓存的过期时间,而是在缓存对象中添加一个逻辑过期时间字段。当访问缓存时,检查逻辑过期时间,如果已过期,使用异步线程更新缓存,当前请求返回旧数据。 - 永不过期,主动更新:对于热门key,不设置过期时间,通过定时任务或其他机制主动更新缓存数据。 - 接口限流:对访问该key的接口进行限流,控制并发请求数量,避免大量请求同时穿透到数据库 [^1][^2][^4]。 ### 缓存雪崩 原理:在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,给数据库带来巨大压力 [^1][^2][^3]。 解决方案: - 分散过期时间:为不同的缓存key设置不同的过期时间,避免大量key在同一时间过期。 - 高可用架构:搭建Redis集群,提高Redis服务的可用性,避免因单点故障导致Redis服务宕机。 - 数据预热:在系统启动前,将一些热门数据提前加载到缓存中,减少缓存失效时的压力。 - 熔断降级:当数据库压力过大时,采取熔断降级策略,如返回默认值或提示信息,避免数据库被压垮 [^1][^2][^3][^4]。 以下是使用Java和Redis实现缓存穿透中缓存空对象的示例代码: ```java import redis.clients.jedis.Jedis; public class CachePenetrationExample { private static final String REDIS_HOST = "localhost"; private static final int REDIS_PORT = 6379; private static final String KEY_PREFIX = "data:"; public static String getData(String id) { Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT); String key = KEY_PREFIX + id; String data = jedis.get(key); if (data == null) { // 模拟从数据库查询数据 String dbData = queryFromDatabase(id); if (dbData == null) { // 缓存空对象,设置较短的过期时间 jedis.setex(key, 60, ""); } else { // 缓存查询到的数据 jedis.set(key, dbData); } data = dbData; } jedis.close(); return data; } private static String queryFromDatabase(String id) { // 模拟数据库查询,这里返回null表示数据不存在 return null; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值