当前位置: 首页 > article >正文

redis缓存穿透

redis缓存穿透

模拟一个缓存穿透的环境:

    • redis缓存穿透
      • 1. 准备一个GET请求并且在第一次访问的时候将数据写入缓存
      • 2. 再次访问的时候首先判断缓存是否命中
      • 3. 命中了直接返回,未命中重建缓存
      • 1. 缓存空对象
      • 2. 布隆过滤器

1. 准备一个GET请求并且在第一次访问的时候将数据写入缓存

// 首先从缓存中获取数据
        Object articleObj = redisTemplate.opsForValue().get(ARTICLE_KEY + id);
        // 拿到缓存了就直接返回
        if (Objects.nonNull(articleObj)){
            String articleJSON = (String) articleObj;
            ApArticle article = JSON.parseObject(articleJSON, ApArticle.class);

            return ResponseResult.okResult(article);
        }

        // 通过数据库获取文章数据
        ApArticle article = getById(id);

        // 重建缓存
        redisTemplate.opsForValue().set(ARTICLE_KEY + id, JSON.toJSONString(article),ARTICLE_EXPIRED);

        // 返回获得的文章数据
        return ResponseResult.okResult(article);

2. 再次访问的时候首先判断缓存是否命中

3. 命中了直接返回,未命中重建缓存

什么事缓存穿透?
缓存穿透其实是一种攻击性的行为。其实是接口访问一个没有被缓存的数据,这个数据每次都会去请求数据库,如果没有一个处理的话,恶意的请求会对数据库造成极大的压力。

模拟一个500并发量的请求,单接口的响应耗时已经达到了1500毫秒
在这里插入图片描述
解决缓存穿透的问题:

1. 缓存空对象

在数据没有命中缓存的时候这个请求会直接的打到后端数据库上,
那么可以对这个没有命中的数据也对应的缓存到redis中,当请求再次来临的时候就不会去访问数据库
缺点:可能会浪费redis大量的内存,并且可能会出现不一致的问题(例如第一次id为2的没有数据,被缓存了空数据,然而后续2插入了数据,再次访问就会有不一致的问题)

2. 布隆过滤器

布隆过滤器
布隆过滤器本质上是一个bitmap
guava的工具包为我们做了布隆过滤器的实现

  1. 添加guava工具包
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
  1. 创建布隆过滤器的Bean并且初始化布隆过滤器。需要使用init方法对布隆过滤器进行数据的初始化,否则布隆过滤器中没有数据,所有的请求来临都会被拒绝掉。
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.model.article.pojos.ApArticle;
import org.apache.hbase.thirdparty.com.google.common.hash.BloomFilter;
import org.apache.hbase.thirdparty.com.google.common.hash.Funnels;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.List;
import static com.heima.model.constant.Constant.BLOOM_FILTER_EXPECTED_SIZE;
import static com.heima.model.constant.Constant.BLOOM_FILTER_FALSE_POSITIVE_RATE;

@Configuration
public class BloomFilterConfig {

    @Autowired
    private ApArticleMapper apArticleMapper;

    public final BloomFilter<Long> bloomFilter;

    public BloomFilterConfig(){
        // 预期元素数量,误判率
        this.bloomFilter = BloomFilter
//                .create(Funnels.unencodedCharsFunnel(),BLOOM_FILTER_EXPECTED_SIZE,BLOOM_FILTER_FALSE_POSITIVE_RATE);
                .create(Funnels.longFunnel(),BLOOM_FILTER_EXPECTED_SIZE,BLOOM_FILTER_FALSE_POSITIVE_RATE);
    }



    @PostConstruct
    public void init(){
        BloomFilter<Long> longBloomFilter = bloomFilter();
        
        // 查询数据库
        List<ApArticle> apArticles = apArticleMapper.selectList(Wrappers.emptyWrapper());

        apArticles.forEach(article->{
            longBloomFilter.put(article.getId());
        });

    }

    @Bean
    public BloomFilter<Long> bloomFilter(){
        // 预期元素数量,误判率
        return bloomFilter;
    }

}

  1. 注入布隆过滤器进行判断。
        // 判断这个id是不是在布隆过滤器中
        boolean mightContain = bloomFilter.mightContain(id);

        // 不存在直接返回
        if (!mightContain){
            return ResponseResult.okResult();
        }

        // 首先从缓存中获取数据
        Object articleObj = redisTemplate.opsForValue().get(ARTICLE_KEY + id);
        // 拿到缓存了就直接返回
        if (Objects.nonNull(articleObj)){
            String articleJSON = (String) articleObj;
            ApArticle article = JSON.parseObject(articleJSON, ApArticle.class);

            return ResponseResult.okResult(article);
        }

        // 通过数据库获取文章数据
        ApArticle article = getById(id);

        // 重建缓存
        if (Objects.nonNull(article)){
            redisTemplate.opsForValue().set(ARTICLE_KEY + id, JSON.toJSONString(article),ARTICLE_EXPIRED,TimeUnit.SECONDS);
            
        }
        
        // 返回获得的文章数据
        return ResponseResult.okResult(article);

http://www.kler.cn/news/107425.html

相关文章:

  • NUUO摄像头远程命令执行漏洞复现 [附POC]
  • 北邮22级信通院数电:Verilog-FPGA(7)第七周实验(2):BCD七段显示译码器(关注我的uu们加群咯~)
  • 睿趣科技:抖音小店申请流程
  • 【每日一题Day361】LC2558从数量最多的堆取走礼物 | 大顶堆
  • 接口自动化测试工具,Postman使用详解
  • Groovy安装开发环境
  • 新手向:如何考虑将数据库技术和大数据框架结合使用?
  • iPhone手机分辨率整理
  • 数据结构—线性表(下)
  • FoLR:Focus on Local Regions for Query-based Object Detection论文学习笔记
  • MES管理系统解决方案实现生产信息全程追溯
  • 『力扣刷题本』:删除排序链表中的重复元素
  • 2023/10/28 JAVA学习
  • 面试题之JavaScript经典for循环(var let)
  • 通过el-tree 懒加载树,创建国家地区四级树
  • 数组与链表算法-矩阵算法
  • FileInputStream文件字节输入流
  • 使用easypoi-spring-boot-starter 4.1.1导入excel报错NoSuchMethodError和NoSuchMethodError
  • python 字符串str与字典dict转换
  • 【Qt】窗口和对话框区别、主窗口和二级窗口区别、QMainWindow和QDialog区别
  • Ubuntu deadsnakes 源安装新版 python
  • 蓝桥杯 Java k倍区间
  • 0047【Edabit ★☆☆☆☆☆】Minimal I: If Boolean Then Boolean
  • RK3588开发笔记-USB3.0接口调试
  • VMware打开共享虚拟机后找不到/mnt/hgfs/文件夹,以及不能拖拽/复制粘贴等操作,ubuntu不能安装VMware tools
  • 3台Centos7快速部署Kafka集群
  • 如何在Node.js中使用环境变量或命令行参数来设置HTTP爬虫ip?
  • 【Proteus仿真】【Arduino单片机】PWM电机调速
  • Mysql的JDBC知识点
  • 【C++的OpenCV】第十四课-OpenCV基础强化(二):访问单通道Mat中的值