Redis知识指南:缓存穿透,缓存击穿,缓存雪崩问题—(5)

本文详细介绍了Redis中的缓存问题,包括缓存雪崩、缓存穿透和缓存击穿的原理及解决方案。缓存雪崩是大量请求在缓存失效时直接击打数据库,可采用加锁或设置不同过期时间来缓解。缓存穿透是查询数据库无结果时,导致每次请求都穿透到数据库,解决办法是将空结果也缓存。缓存击穿针对特定key的高并发访问,可通过锁或异步更新缓存来应对。

Redis知识指南:缓存穿透,缓存击穿,缓存雪崩问题—(5)

本节内容主要讲的是Redis涉及到面试的一些高频词汇。本人还是名学生,希望能够将这些问题记录起来

1、缓存的概念

广义缓存:缓存就是第一次加载某些数据时,这些数据有可能会被复用。在加载数据的同时,还将数据放到一个指定的地点作为保存。在下次加载时,就会从指定的地点取数据。

当然,如果从上述所谓的地方取数据比自己加载数据还要慢,就失去了加载缓存的意义。所以加载缓存是有一个前提。

狭义的缓存分为以下三类:

  1. 虚拟机缓存
  2. 分布式缓存
  3. 数据库缓存

一般而言,数据缓存速度从上到下由快到慢。

在这里插入图片描述

2、缓存雪崩

缓存雪崩的简单理解就是:原有的缓存失效或者数据未加载到缓存中。在新缓存未到期间,所有**原本应当访问缓存的请求全都去查询数据库了。**这会造成对数据库CPU和内存造成巨大压力,严重甚至会出现数据库宕机造成系统崩溃。

正常的图如下:

在这里插入图片描述

缓存失效时如下图:

在这里插入图片描述

解决方法:

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。虽然能够在一定的程度上缓解了数据库的压力但是与此同时又降低了系统的吞吐量。

public Users getByUsers(Long id) {
    // 1.先查询redis
    String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName()+ "-id:" + id;
    String userJson = redisService.getString(key);
    if (!StringUtils.isEmpty(userJson)) {
        Users users = JSONObject.parseObject(userJson, Users.class);
        return users;
    }
    Users user = null;
    try {
        lock.lock();
        // 查询db
        user = userMapper.getUser(id);
        redisService.setSet(key, JSONObject.toJSONString(user));
    } catch (Exception e) {
    } finally {
        lock.unlock(); // 释放锁
    }
    return user;
}
  1. 加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,如果同时过来1000个请求,那么999个都在阻塞的。同样会导致用户等待超时,所以这是个治标不治本的方法。
  2. 分析用户的行为。不同的key设置不同的过期时间,让缓存失效的时间点尽量均匀。

3、缓存穿透

缓存穿透是指用户查询数据时,数据库没有,缓存也没有。这样子用户在进行查询时,在缓存中找不到就要去数据库中再查询一遍。可是都没有就只能返回空值。这样请求就穿过了缓存就直接找数据库了。这就是缓存命中率的问题

解决办法无非就是提高缓存的命中率。方法如下:

  1. 第一次查询结果也为空的时候,设置一个默认值存放到缓存中。第二次到缓存中获取就有值了而不会再访问数据库。这种方法最为简单粗暴。
  2. 将第一次空结果也缓存起来,下次同样的请求就可以直接返回空了,既可以避免当查询为空值时引起的缓存穿透,又可以单独设置个缓存区域存储空值,对要查询的key进行预先校验,然后放到后面的正常缓存处理逻辑。
public String getByUsers2(Long id) {
// 1.先查询redis
String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()
[1].getMethodName()+ "-id:" + id;
String userName = redisService.getString(key);
if (!StringUtils.isEmpty(userName)) {
return userName;
}
System.out.println("######开始发送数据库DB请求########");
Users user = userMapper.getUser(id);
String value = null;
if (user == null) {
// 标识为null
value = "";
} else {
value = user.getName();
}
redisService.setString(key, value);
return value;
}

4、缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。 这个时候,需要考虑一个问题:缓存被“击穿”的问题。这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是 很多key。

这里的思想简单的来说,一般很多人玩英雄联盟都会大部分人选择所谓大区、”一区“。其他地方的区能够正常进入但是它们同时被登陆上去造成的拥挤。

使用锁,单机用synchronized,lock等,分布式用分布式锁

缓存过期时间不设置,而是设置在key对应的value里。如果检测到存的时间超过过期时间则异步更新缓存。

以上的一些关于特别情况的简单介绍,谢谢大家的阅读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao艾扶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值