【Redis缓存:常见问题及解决方案】

目录

①缓存雪崩

常见的解决方案

加锁排队

随机化过期时间

设置⼆级缓存

②缓存穿透

常见的解决方案

布隆过滤器

缓存空结果

接口层增加校验

③缓存击穿

常见的解决方案

加锁排队

设置热点数据永远不过期

分布式缓存系统

④缓存预热

缓存预热的实现思路 


①缓存雪崩

缓存雪崩是指在短时间内,有大量缓存同时过期,导致⼤量的请求直接查询数据库,从⽽对数据库造成了巨⼤的压⼒,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
我们先来看下正常情况下和缓存雪崩时程序的执⾏流程图,正常情况下系统的执⾏流程如下图所示:

缓存雪崩的执⾏流程,如下图所示:

以上对⽐图可以看出缓存雪崩对系统造成的影响,那如何解决缓存雪崩的问题?

常见的解决方案

加锁排队

加锁排队可以起到缓冲的作⽤,防⽌⼤量的请求同时操作数据库,但它的缺点是增加了系统的响应时间,降低了系统的吞吐量,牺牲了⼀部分⽤户体验。

随机化过期时间

为了避免缓存同时过期,可在设置缓存时添加随机时间,这样就可以极⼤的避免⼤量的缓存同时失效。示例代码如下:

// 缓存原本的失效时间
int exTime = 10 * 60;
// 随机数⽣成类
Random random = new Random();
// 缓存设置
jedis.setex(cacheKey, exTime+random.nextInt(1000) , value);

设置⼆级缓存

⼆级缓存指的是除了 Redis 本身的缓存,再设置⼀层缓存,当 Redis 失效之后,先去查询⼆级缓存。
例如可以设置⼀个本地缓存,在 Redis 缓存失效的时候先去查询本地缓存⽽⾮查询数据库。
加⼊⼆级缓存之后程序执⾏流程,如下图所示:

②缓存穿透

缓存穿透是指查询数据库和缓存都无数据,因为数据库查询⽆数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。

缓存穿透执行流程如下图所示:

其中红⾊路径表示缓存穿透的执⾏路径,可以看出缓存穿透会给数据库造成很⼤的压⼒。

常见的解决方案

布隆过滤器

布隆过滤器是一种概率型数据结构,其特点是高效地插入和查询,可以用来告诉你“某样东西一定不存在或者可能存在”。

缓存空结果

对于数据库中不存在的数据,也对其在缓存中设置默认值Null,为避免占用资源,一般过期时间会比较短,将空结果的缓存时间设置的短⼀些,例如 3-5 分钟。这种方案相对简单,但是也容易破解,比如攻击者通过分析数据格式,不重复的请求数据库不存在数据,那这种方案就等于失效的。

接口层增加校验

如用户鉴权校验,id做基础校验,id<=0的直接拦截;从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒。

③缓存击穿

缓存击穿指的是某个热点(key)缓存在某⼀时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨⼤的压⼒,这种情况就叫做缓存击穿。

缓存击穿的执⾏流程如下图所示:

区分缓存穿透:

缓存击穿发生在某个热点(key)缓存过期后,缓存穿透发生在缓存和数据库中都没有数据时

常见的解决方案

加锁排队

此处理⽅式和缓存雪崩加锁排队的⽅法类似,都是在查询数据库时加锁排队,缓冲操作请求以此来减少服务器的运⾏压⼒。

设置热点数据永远不过期

对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性,但需要注意在数据更改之后,要及时更新此热点缓存,不然就会造成查询结果的误差。

分布式缓存系统

当热点数据过期后,其他线程来获取数据的时候,如果缓存中没有这个数据,那么我们就通知所有的节点来查询数据库,并把查询结果设置到缓存中。这样,即使热点数据过期了,由于缓存系统中存在这个数据,所以也不会发生缓存击穿。 

④缓存预热

⾸先来说,缓存预热并不是⼀个问题,⽽是使⽤缓存时的⼀个优化⽅案,它可以提⾼前台⽤户的使⽤体验。
缓存预热指的是在系统启动的时候,先把查询结果预存到缓存中,以便⽤户后⾯查询时可以直接从缓存中读取,以节约⽤户的等待时间
缓存预热的执⾏流程,如下图所示:

缓存预热的实现思路

  1. 把需要缓存的⽅法写在系统初始化的方法中,这样系统在启动的时候就会⾃动的加载数据并缓存数据
  2. 把需要缓存的⽅法挂载到某个页面或后端接口上手动触发缓存预热
  3. 设置定时任务,定时⾃动进⾏缓存预热。