Redis之缓存穿透,雪崩,击穿解读

举报
yd_249383650 发表于 2023/09/28 11:21:32 2023/09/28
【摘要】 ​ 目录缓存穿透定义解决方案方案1:空对象缓存或者缺省值 方案2:布隆过滤器缓存击穿定义解决方案方案1:热点数据永不过期方案2:互斥独占锁防止击穿缓存雪崩 定义解决方案方案1:过期时间设置随机方案2:缓存预热方案3:Redis集群 缓存穿透定义当我们请求去查询一条记录,先到redis中查询后到mysql查询都发现找不到该条记录,但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,这些请...

 

目录

缓存穿透

定义

解决方案

方案1:空对象缓存或者缺省值

 方案2:布隆过滤器

缓存击穿

定义

解决方案

方案1:热点数据永不过期

方案2:互斥独占锁防止击穿

缓存雪崩 

定义

解决方案

方案1:过期时间设置随机

方案2:缓存预热

方案3:Redis集群 



缓存穿透

定义

当我们请求去查询一条记录,先到redis中查询后到mysql查询都发现找不到该条记录,但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,这些请求像“穿透”了缓存一样直接打在数据库上,这种现象就叫做缓存穿透。这种现象我们称为缓存穿透,这个redis变成了一个摆设。

恶意对网站进行攻击时,拿一个不存在的id去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉

解决方案

方案1:空对象缓存或者缺省值

一旦发生缓存穿透,我们就可以针对查询的数据,在Redis中缓存一个空值或是和业务层协商确定的缺省值(例如,库存的缺省值可以设为0) 。紧接着,应用发送的后续请求再进行查询时,就可以直接从Redis中读取空值或缺省值,返回给业务应用了,避免了把大量请求发送给数据库处理,保持了数据库的正常运行。

如果Redis查不到数据,数据库也查不到,我们把这个Key值保存进Redis,设置value="null",当下次再通过这个Key查询时就不需要再查询数据库。这种处理方式肯定是有问题的,假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。而且随着缓存的暑假的越来越多,对Redis的压力越来越大,可以对缓存的Key有个过期的时间分担压力。

 方案2:布隆过滤器

  • 把已存在数据的key存在布隆过滤器中,相当于redis前面挡着一个布隆过滤器。
  • 当有新的请求时,先到布隆过滤器中查询是否存在:
  • 如果布隆过滤器中不存在该条数据则直接返回;
  • 如果布隆过滤器中已存在,才去查询缓存redis,如果redis里没查询到则再查询Mysql数据库

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。

一句话就是:由一个初始值为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素。

缓存击穿

定义

大量的请求同时查询一个key时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去(简单说就是热点key突然失效了,暴打mysql)。

造成缓存击穿的主要原因就是:我们为缓存中的数据设置了过期时间。如果在某个时刻从数据库获取了大量的数据,并设置了相同的过期时间,这些缓存的数据就会在同一时刻失效,造成缓存击穿问题

  1. 时间到了自然清除,但还被访问到了
  2. 删除key的时候,突然被访问到了

解决方案

方案1:热点数据永不过期

对于热点数据,如果业务允许的话,对于热点的key可以设置永不过期的key。

方案2:互斥独占锁防止击穿

使用分布式锁思想,保证对于每个 Key 同时只有一个线程去查询后端的服务,某个线程在查询后端服务的同时,其他线程没有获得分布式锁的权限,需要进行等待,查询到以后回写Redis,其他请求线程再到Redis中查询,当然这样会导致系统的性能变差。

示例java代码如下:

public User findUserById2(Integer id) {
    User user = null;
    String key = CACHE_KEY_USER+id;

    //1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql
    user = (User) redisTemplate.opsForValue().get(key);
    if(user == null) {
        //2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysql
        synchronized (UserService.class){
            user = (User) redisTemplate.opsForValue().get(key);
            //3 二次查redis还是null,可以去查mysql了(mysql默认有数据)
            if (user == null) {
                //4 查询mysql拿数据
                user = userMapper.selectByPrimaryKey(id);//mysql有数据默认
                if (user == null) {
                    return null;
                }else{
                    //5 mysql里面有数据的,需要回写redis,完成数据一致性的同步工作
                    //setnx 不存在才创建
                    redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);
                }
            }
        }
    }
    return user;
}

缓存雪崩 

定义

​缓存雪崩是指缓存数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库,还有一种情况是Redis主机挂了,Redis全盘崩溃

 在某一时刻缓存集中失效,或者缓存系统出现故障,所有的并发流量就会直接到达数据库。数据存储层的调用量就会暴增,用不了多长时间,数据库就会被大流量压垮

解决方案

方案1:过期时间设置随机

在原有的失效时间上加上一个随机值,比如1-5分钟随机。这样就避免了因为采用相同的过期时间导致的缓存雪崩。

方案2:缓存预热

 缓存预热就是将数据提前加入到缓存中,当数据发生变更,再将最新的数据更新到缓存

方案3:Redis集群 

为了防止Redis宕机导致缓存雪崩的问题,可以搭建Redis集群,提高Redis的容灾性。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。