Redis应用(7)——Redis的项目应用(六):布隆过滤器---白名单 ----> Reids的问题,雪崩/ 击穿 / 穿透【重要】 布隆过滤器

news2024/11/22 16:08:16

目录

  • 引出
  • Redis的问题
    • 缓存雪崩:key不存在
    • 缓存击穿:热点key
    • 缓存穿透【重要】
  • 穿透的解决方案:布隆过滤器
    • 问题:如何存储100w纯数字
    • 布隆过滤器
    • 项目应用:布隆过滤器≈白名单
      • htool工具包案例
  • Redis项目应用(六)布隆过滤器---白名单
    • 业务逻辑
    • 布隆过滤器工具类 BitMapBloomFilter
    • 缓存预热+布隆过滤器初始化@Scheduled(cron = "0 01 18 * * ?")
    • service层:布隆-->缓存-->数据库
    • controller层:布隆无,非法;布隆有-->service
    • 测试结果模拟
      • 1.布隆过滤器未通过,非法请求
      • 2.布隆过滤器通过,缓存中查询成功
      • 3.布隆过滤器通过,缓存没有,从数据库查询
  • 总结

引出


1.Redis的问题,缓存雪崩,key不存在;击穿,热点key;穿透,redis没有,数据库没有;
2.布隆过滤器,返回没有,则结论一定正确;返回有,结论不一定正确;
3.布隆过滤器可以作为项目的白名单,htool工具包的使用;
4.项目应用BitMapBloomFilter;缓存预热+布隆过滤器初始化@Scheduled(cron = “0 01 18 * * ?”);
5.流程:布隆–>缓存–>数据库;

Redis的问题

在这里插入图片描述

缓存雪崩:key不存在

让key的失效时间随机

大量请求访问redis,redis的key不存在,数据大量请求发送到数据库,数据库压力瞬间变大;

当大量请求访问redis,redis的key不存在,大量请求打向数据库。

在这里插入图片描述

解决方案; 在过期时间设置为随机,防止同一时间消失。

redisTemplate.opsForValue().set("users", users, 
          new Random().nextInt(7) + 29, TimeUnit.MINUTES);

在这里插入图片描述

缓存击穿:热点key

让key永不过期

某个key访问量突然暴增,微博某个词条访问量瞬间很大,一直转圈
此时,数据库一直查询某个key,一直查询某个值;

在这里插入图片描述

解决方案:让key 永不过期

缓存穿透【重要】

指客户端请求的数据在缓存和数据库中都没有,这样缓存永远不会生效,这些请求都会打到数据库;

如果有人查询100w条数据都打到数据库,数据库就会瘫痪;

在这里插入图片描述

在这里插入图片描述

穿透的解决方案:布隆过滤器

在这里插入图片描述

布隆过滤器,就是一种数据结构,它是由一个长度为m bit的位数组与n个hash函数组成的数据结构,位数组中每个元素的初始值都是0。在初始化布隆过滤器时,会先将所有key进行n次hash运算,这样就可以得到n个位置,然后将这n个位置上的元素改为1。这样,就相当于把所有的key保存到了布隆过滤器中了。

结论:如果布隆过滤器中没有,则一定没有,常用作白名单;

即:布隆过滤器返回值:
如果布隆过滤器返回没有,则结论正确,阻止访问;
如果布隆过滤器返回有,则结论不一定正确;

项目应用:
如果白名单中没有,则对方恶意访问缓存,阻止访问;

问题:如何存储100w纯数字

在连续的空间存储100w个纯数字占用多少内存

package com.tianju.redisDemo.testDemo;

public class BigMapDemo {
    public static void main(String[] args) {
        int[] a = {1,2,3,4,5,6,7,8,9};
        int[] b = new int[1000000]; // 存100w个数字
        // 占用空间,1个int占用32 bits空间
        System.out.println(32*1000000.0/1024/1024+"MB");
    }
}

int— 32bits
100w x 32bits= 32,000,000(bits)
1kb = 1024 bits;
1M = 1024kb

结论:100w个纯数字占用了30MB的内存;

解决办法,用BigMap,1int,对应32个bits,可以用来表示0~31个数字,如下图所示,可以表示数字1,5;大大节省了空间

在这里插入图片描述

布隆过滤器

布隆过滤器(Bloom Filter)本质上是由长度为 m 的位向量或位列表(仅包含 0 或 1 位值的列表)组成,最初所有的值均设置为 0,如下图所示。

在这里插入图片描述

为了将数据项添加到布隆过滤器中,会提供 K 个不同的哈希函数,并将结果位置上对应位的值置为 “1”。

在这里插入图片描述

如上图所示,当输入 “semlinker” 时,预设的 3 个哈希函数将输出 2、4、6,我们把相应位置 1。假设另一个输入 ”kakuqo“,哈希函数输出 3、4 和 7。你可能已经注意到,索引位 4 已经被先前的 “semlinker” 标记了。此时,我们已经使用 “semlinker” 和 ”kakuqo“ 两个输入值,填充了位向量。当前位向量的标记状态为:

在这里插入图片描述

当对值进行搜索时,与哈希表类似,我们将使用 3 个哈希函数对 ”搜索的值“ 进行哈希运算,并查看其生成的索引值。假设,当我们搜索 ”fullstack“ 时,3 个哈希函数输出的 3 个索引值分别是 2、3 和 7:

在这里插入图片描述

从上图可以看出,相应的索引位都被置为 1,这意味着我们可以说 ”fullstack“ 可能已经插入到集合中。事实上这是误报的情形,产生的原因是由于哈希碰撞导致的巧合而将不同的元素存储在相同的比特位上。幸运的是,布隆过滤器有一个可预测的误判率(FPP):

在这里插入图片描述

  • n 是已经添加元素的数量;
  • k 哈希的次数;
  • m 布隆过滤器的长度(如比特数组的大小);

极端情况下,当布隆过滤器没有空闲空间时(满),每一次查询都会返回 true 。这也就意味着 m 的选择取决于期望预计添加元素的数量 n ,并且 m 需要远远大于 n 。

实际情况中,布隆过滤器的长度 m 可以根据给定的误判率(FFP)的和期望添加的元素个数 n 的通过如下公式计算:

在这里插入图片描述

了解完上述的内容之后,我们可以得出一个结论,

当我们搜索一个值的时候,

若该值经过 K 个哈希函数运算后的任何一个索引位为 ”0“,那么该值肯定不在集合中。

但如果所有哈希索引值均为 ”1“,则只能说该搜索的值可能存在集合中

项目应用:布隆过滤器≈白名单

在这里插入图片描述

如果布隆过滤器中没有,则一定没有,可以证明没有;

结论:
如果布隆过滤器返回没有,这个结论一定正确;
如果布隆过滤器返回有,这个结论不一定正确;

如果白名单中没有,则对方恶意访问缓存,则不通过,阻止访问;

htool工具包案例

https://www.bookstack.cn/read/hutool-5.6.0-zh

在这里插入图片描述

    // 初始化
    BitMapBloomFilter filter = new BitMapBloomFilter(10);
    filter.add("123");
    filter.add("abc");
    filter.add("ddd");
    // 查找
    filter.contains("abc")

Redis项目应用(六)布隆过滤器—白名单

业务逻辑

后台查询用户,防止穿透缓存到数据库查询用户,导致数据库压力大;

  • 预热的时候,用户信息加载到布隆过滤器中;
  • 查询业务,比如查询用户时,先用布隆过滤器过滤,再去redis;

在这里插入图片描述

在这里插入图片描述

以一个根据用户名查询用户是否在数据库中案例为例;

1.先到布隆过滤器中,在controller层解决,拦阻非法请求;
2.通过布隆过滤器,在redis缓存中查找,查询成功返回;
3.通过布隆过滤器,在redis缓存中没找到,查询数据库,查询成功返回,并且更新缓存

布隆过滤器工具类 BitMapBloomFilter

WhiteListBloomFilter.java工具类

package com.tianju.redisDemo.util;

import cn.hutool.bloomfilter.BitMapBloomFilter;

/**
 * 白名单的布隆过滤器
 */
public class  WhiteListBloomFilter {

    // Params: m – M值决定BitMap的大小
    private static BitMapBloomFilter bloomFilter = new BitMapBloomFilter(10);

    // TODO:布隆过滤器有,缓存没有,需要从数据库查询

    /**
     * 将str加入布隆过滤器中,作为白名单使用
     * @param str
     */
    public static void addBloom(String str){
        bloomFilter.add(str);
    }

    /**
     * 判断str是否在布隆过滤器中
     * @param str
     * @return true表示可能在布隆过滤器中;false表示一定不在布隆过滤器中
     */
    public static Boolean isInBloom(String str){
        return bloomFilter.contains(str);
    }
}

缓存预热+布隆过滤器初始化@Scheduled(cron = “0 01 18 * * ?”)

UsernamesPreHot.java缓存预热

package com.tianju.redisDemo.job;

import com.tianju.redisDemo.dao.UserMapper;
import com.tianju.redisDemo.entity.User;
import com.tianju.redisDemo.util.WhiteListBloomFilter;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.Schedules;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

/**
 * 采用 @Scheduled(cron = "0 54 21 * * ?")进行预热
 */
@Slf4j
@Component
public class UsernamesPreHot {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserMapper userMapper;

    @Scheduled(cron = "0 01 18 * * ?")
    public void preHot() {
        // 1.清除缓存中的数据
        stringRedisTemplate.delete("usernames");

        // 2.更新缓存中的数据
        // 3.也要加入到布隆过滤器中
        List<User> userList = userMapper.selectList(null);
        // TODO:布隆过滤器有,缓存没有,需要从数据库查询
        WhiteListBloomFilter.addBloom("Arya");
        userList.forEach(user ->{
                    stringRedisTemplate.opsForSet().add("usernames", user.getUsername());
                    // 放到布隆过滤器中
                    WhiteListBloomFilter.addBloom(user.getUsername());
                }
        );
        log.debug("redis缓存预热 + 布隆过滤器预热,usernames");
    }
}

service层:布隆–>缓存–>数据库

package com.tianju.redisDemo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianju.redisDemo.dao.UserMapper;
import com.tianju.redisDemo.entity.User;
import com.tianju.redisDemo.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Transactional
@Slf4j
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String findByUsername(String username) {
        // 1.先到布隆过滤器中,在controller层解决

        // 2.然后到redis中
        log.debug(">>>> 是否在redis缓存中");
        Boolean isInRedis = stringRedisTemplate.opsForSet().isMember("usernames", username);
        if (isInRedis){ // 缓存中有
            log.debug(username+"在redis缓存中查找成功,,,");
            Set<String> usernames = stringRedisTemplate.opsForSet().members("usernames");
            return usernames.stream().filter(u -> username.equals(u)).collect(Collectors.toList()).get(0);

            // 3.最后到数据库
        }else { // 缓存中也没有,到数据库查询
            log.debug("缓存中不存在,从数据库中进行读取....");
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("username", username); // 列名和参数

            User user = userMapper.selectOne(wrapper);
            log.debug("数据库查询成功,更新缓存....");
            stringRedisTemplate.opsForSet().add("usernames", user.getUsername());
            return user.getUsername();
        }
    }
}

controller层:布隆无,非法;布隆有–>service

package com.tianju.redisDemo.controller;

import com.tianju.redisDemo.dto.HttpResp;
import com.tianju.redisDemo.dto.ResultCode;
import com.tianju.redisDemo.service.IUserService;
import com.tianju.redisDemo.util.WhiteListBloomFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {
    
    @Autowired
    private IUserService userService;

    @GetMapping("findByUsername")
    public HttpResp findByUsername(String username){
        // 1.先过布隆过滤器
        Boolean inBloom = WhiteListBloomFilter.isInBloom(username);

        // 如果布隆过滤器里没有,则一定没有,非法请求
        if (!inBloom){
            log.debug(">>>>>非法请求。布隆过滤器未通过");
            return HttpResp.results(ResultCode.USER_FIND_ERROR,new Date(),"非法用户禁止操作");
        }
        
        // 然后到缓存 --- 》 到数据库
        String byUsername = userService.findByUsername(username);
        return HttpResp.results(ResultCode.BOOK_RUSH_SUCCESS,new Date(),byUsername);

    }
}

测试结果模拟

在这里插入图片描述

1.布隆过滤器未通过,非法请求

在这里插入图片描述

2.布隆过滤器通过,缓存中查询成功

在这里插入图片描述

3.布隆过滤器通过,缓存没有,从数据库查询

一开始数据库

在这里插入图片描述

现在布隆过滤器器里有,数据库里面有,缓存没有

在这里插入图片描述

数据库后台改动

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


总结

1.Redis的问题,缓存雪崩,key不存在;击穿,热点key;穿透,redis没有,数据库没有;
2.布隆过滤器,返回没有,则结论一定正确;返回有,结论不一定正确;
3.布隆过滤器可以作为项目的白名单,htool工具包的使用;
4.项目应用BitMapBloomFilter;缓存预热+布隆过滤器初始化@Scheduled(cron = “0 01 18 * * ?”);
5.流程:布隆–>缓存–>数据库;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/781767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OpenCV4图像处理-图像交互式分割-GrabCut

本文将实现一个与人&#xff08;鼠标&#xff09;交互从而分割背景的程序。 GrabCut 1.理论介绍2. 鼠标交互3. GrabCut 1.理论介绍 用户指定前景的大体区域&#xff0c;剩下为背景区域&#xff0c;还可以明确指出某些地方为前景或者背景&#xff0c;GrabCut算法采用分段迭代的…

蓝桥杯专题-真题版含答案-【星系炸弹】【隔行变色】【手链样式】【生日蜡烛】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

Python爬虫-进入浏览器控制台就出现无限debugger,怎么解决?

前言 本文是该专栏的第53篇,后面会持续分享python爬虫干货知识,记得关注。 对于控制台出现无限debugger的情况,笔者之前在“JS逆向-常见反调试之“无限Debugger”,怎么解决?”里面有详细介绍过。而本文,针对控制台调试出现无限debugger的另一种情况,笔者来详细介绍该问…

C语言的自定义类型(结构体、枚举、联合体)

“虽然前方拥堵&#xff0c;但您仍在最优路线上” ——高德地图 文章目录 一、结构体类型 1.结构体类型的定义 2.结构体变量的初始化 3.结构体类型变量的定义 4.结构体内存对齐 5.结构体实现位段 二、枚举类型 三、联合体类型 大家好&#xff0c;我是纪宁。 这篇文章主…

【数据结构】--八大排序算法【完整版】

匠心制作&#xff0c;后续有问题会加以修改的 &#xff0c;全文均是自己写的&#xff0c;几张图有参考网络 ———————————————— 目录 一、直接插入排序 二、希尔排序(直接插入排序的改良版) 三、选择排序&#xff08;直接选择排序&#xff09; 四、堆排序 …

通过YOLOV5实现:王者荣耀百里守约自瞄

前期提要&#xff1a; 本文章仅供技术讨论使用。 关于如何通过YOLOV5去检测到王者中的敌方人物&#xff0c;在网上有很多相关的文章和教学视频我在这里就不过多的阐述&#xff0c;本篇文章主要讲的是在实现中比较难处理的一些技术点&#xff1a;如何获取高刷新率的手机屏幕、…

Hive 调优集锦(1)

一、前言 1.1 概念 Hive 依赖于 HDFS 存储数据&#xff0c;Hive 将 HQL 转换成 MapReduce 执行&#xff0c;所以说 Hive 是基于Hadoop 的一个数据仓库工具&#xff0c;实质就是一款基于 HDFS 的 MapReduce 计算框架&#xff0c;对存储在HDFS 中的数据进行分析和管理。 1.2 架…

贤鱼的刷题日常(数据结构链表学习)-1748:约瑟夫问题--题目详解

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;例题讲解1748:约瑟夫问题 ✅创作者&#xff1a;贤鱼 ⏰预计时间&#xff1a;15分钟 &#x1f389;个人主页&#xff1a;贤鱼的个人主页 &#x1f525;专栏系列&#xff1a;c &#x1f341;贤鱼的个人社区&#xff0c;欢迎你…

NLP实战8:图解 Transformer笔记

目录 1.Transformer宏观结构 2.Transformer结构细节 2.1输入 2.2编码部分 2.3解码部分 2.4多头注意力机制 2.5线性层和softmax 2.6 损失函数 3.参考代码 &#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#…

Okhttp-LoggingInterceptor的简单使用

概述 Okhttp除了提供强大的get,post网络请求外&#xff0c;还包含请求日志的拦截器&#xff0c;可以监视&#xff0c;重写&#xff0c;重试调用请求。 简单使用 我们在构造OkHttpClient时&#xff0c;通过addInterceptor()方法添加我们需要的过滤器。 object OkhttpUtils{……

SpringBoot知识范围-学习步骤【JSB系列之000】

语言视频选择收录专辑链接C张雪峰推荐选择了计算机专业之后-在大学期间卷起来-【大学生活篇】JAVA黑马B站视频JAVA部分的知识范围、学习步骤详解JAVAWEB黑马B站视频JAVAWEB部分的知识范围、学习步骤详解SpringBootSpringBoot知识范围-学习步骤【JSB系列之000】微信小程序详细解…

【stable diffusion】保姆级入门课程04-Stable diffusion(SD)图生图-局部重绘的用法

目录 0.本章素材 1.什么是局部重绘 2.局部重绘和涂鸦有什么不同 3.操作界面讲解 3.1.蒙版模糊 3.2.蒙版模式 3.3.蒙版蒙住的内容 3.4.重绘区域 4.局部重绘的应用&#xff08;面部修复&#xff09; 5.课后训练 0.本章素材 chilloutmix模型(真人模型)百度地址&#xf…

数据结构—树状数组

树状数组 单点修改、区间查询区间修改、单点查询区间修改、区间查询 单点修改、区间查询 这里讲解树状数组的最基本操作单点修改、区间查询&#xff0c;当然能做到单点修改、区间查询&#xff0c;肯定就能做到单点修改、单点查询了。树状数组是用来快速求前缀和的&#xff0c;…

MGRE之OSPF实验

目录 题目&#xff1a; 步骤二&#xff1a;拓扑设计与地址规划​编辑 步骤三&#xff1a;IP地址配置 步骤四&#xff1a;缺省路由配置 步骤五&#xff1a;NAT的配置 步骤六&#xff1a;MGRE配置 中心站点R1配置 分支站点配置 中心站点R5 R1配置 分支站点配置 检测&…

UE 材质学习补充

Add Name Reroute Node ...&#xff08;本地变量&#xff09; 该节点可以整理节点&#xff0c;优化界面 Texture Texture(纹理图像)&#xff0c;一般由RGB三个通道混合构成&#xff0c;RGB三个通道的值代表亮度&#xff0c;RGB三个通道分别都是0-1&#xff08;0-255&#xff09…

征服FarmerJohn(二) Naptime【USACO05JAN】

题解目录 前言题目内容题目描述输入输出样例题目思路示例代码AC图片 后记往期精彩 前言 在上一期征服FarmerJohn&#xff08;一&#xff09;三角形【USACO2020FEB-B】结束之后&#xff0c;我们来看一道难度有所提升的DP问题&#xff0c;也就是常说的动态规划&#xff0c;今天我…

Please set the ROCKETMQ_HOME variable in your environment!

原因 启动ROCKETMQ执行命令start mqnamesrv.cmd时报错 翻译意思是请在您的环境中设置ROCKETMQ_HOME变量&#xff01; 查看mqnamesrv.cmd可以看到如果"%ROCKETMQ_HOME%\bin\runserver.cmd"不存在会报此错误 配置上环境变量ROCKETMQ_HOME即可

《深入理解计算机系统》(美)布赖恩特(Bryant,R.E.) 等

适合对象&#xff1a;对计算机感兴趣的朋友。 需要相关资料的可私信我。 持续更新中&#xff1a; 第一章&#xff1a;计算机系统漫游 主要知识点&#xff1a;解读全书结构框架&#xff0c;解释OS的原理和相关硬件软件。计算机系统是由硬件和系统软件组成&#xff0c;共同协作…

kafka消费者api和分区分配和offset消费

kafka消费者 消费者的消费方式为主动从broker拉取消息&#xff0c;由于消费者的消费速度不同&#xff0c;由broker决定消息发送速度难以适应所有消费者的能力 拉取数据的问题在于&#xff0c;消费者可能会获得空数据 消费者组工作流程 Consumer Group&#xff08;CG&#x…

如何在 SwiftUI 中使用 Touch ID 和 Face ID?

1. 需要通过指纹&#xff0c;面容认证后才能打开 App 2. 添加配置 需要向 Info.plist 文件中添加一个配置&#xff0c;向用户说明为什么要访问 添加 Privacy - Face ID Usage Description 并为其赋予值 $(PRODUCT_NAME) need Touch Id or Face ID permission for app lock 3. …