重学SpringBoot3-集成Redis(五)之布隆过滤器

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》
期待您的点赞👍收藏⭐评论✍

重学SpringBoot3-集成Redis(五)之布隆过滤器

  • 1. 什么是布隆过滤器?
    • 基本概念
    • 适用场景
  • 2. 使用 Redis 实现布隆过滤器
    • 项目依赖
    • Redis 配置
  • 3. 创建布隆过滤器服务
  • 4. 布隆过滤器的初始化
  • 5. 使用布隆过滤器进行缓存穿透防护
  • 6. 测试效果
    • 6.1. 启动项目
    • 6.2. 查询存在的商品
    • 6.3. 查询不存在的商品
  • 7. 总结

在高并发场景下,缓存是提升系统性能的重要手段。然而,常规缓存机制中,若遇到大量无效请求访问(请求的 key 不存在于缓存或数据库),就会导致 缓存穿透。为了应对这种问题,布隆过滤器缓存空值 是应对缓存穿透的两大主流方案,布隆过滤器适用于大规模、复杂场景,缓存空值适用于小规模场景。布隆过滤器(Bloom Filter) 能够通过哈希算法判断一个 key 是否可能存在,减少无效请求对数据库的压力。

本篇博客将介绍如何使用 Spring Boot 3Redis 实现布隆过滤器,并结合示例代码来详细讲解布隆过滤器的原理和在 Redis 中的实现方式。

1. 什么是布隆过滤器?

基本概念

布隆过滤器是一种空间效率高的 概率性数据结构,用于快速判断某个元素是否在集合中。它有以下特点:

  • 内存占用小:相比传统的集合结构,布隆过滤器的内存使用更少。
  • 可能存在误判:布隆过滤器只能确定某个元素“可能存在”或“绝对不存在”。但存在误判的概率可以通过调整参数降低。
  • 不支持删除:布隆过滤器不支持删除已添加的元素,删除某个元素会导致误判率增加。

图片来源:https://systemdesign.one/bloom-filters-explained/

图片来源:https://systemdesign.one/bloom-filters-explained/

适用场景

布隆过滤器在以下场景中非常适用:

  • 防止缓存穿透:将不存在的 key 存储在布隆过滤器中,避免大量无效请求直接查询数据库。
  • 防止重复数据:在大规模数据处理中,使用布隆过滤器避免重复处理相同的数据。

2. 使用 Redis 实现布隆过滤器

Redis 提供了开箱即用的布隆过滤器功能,通过 Redis 的插件 RedisBloom,安装过程参考:Redis安装RedisBloom,我们可以非常方便地使用布隆过滤器存储和管理 key。

项目依赖

首先,在 Spring Boot 项目中引入相关依赖,可参考之前文章。需要 Redis 的支持,以及 Spring Data Redis 来实现与 Redis 的交互。

注意: Redisson 提供了对布隆过滤器的支持,具体实现会利用它的 API。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.20.0</version>
</dependency>

Redis 配置

application.yml 文件中配置 Redis 的连接信息,详细请参考上一章重学SpringBoot3-集成Redis(四)之Redisson,进行 Redisson 配置。

spring:
  redis:
    redisson:
      config: |
        singleServerConfig:
          address: redis://1.94.26.81:6379   # Redis 连接地址,前缀为 redis://
          password: redis123456              # 如果 Redis 需要密码认证,则填写密码
          timeout: 3000                      # 命令执行超时时间(毫秒)

配置类中初始化 Redisson 客户端:

package com.coderjia.boot310redis.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.starter.RedissonProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author CoderJia
 * @create 2024/10/5 下午 04:53
 * @Description
 **/
@Configuration
public class RedissonConfig {

    @Autowired
    private RedissonProperties redissonProperties;

    @Bean
    public RedissonClient redissonClient() throws Exception{
        Config config = Config.fromYAML(redissonProperties.getConfig());
        Redisson.create(config);
        System.out.println("Redisson 已启动");
        return Redisson.create(config);
    }

}

3. 创建布隆过滤器服务

接下来,我们需要定义一个服务来管理布隆过滤器。利用 Redisson 提供的 API,可以轻松实现布隆过滤器。

import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BloomFilterService {

    private static final String BLOOM_FILTER_NAME = "bloomFilter";

    @Autowired
    private RedissonClient redissonClient;

    // 初始化布隆过滤器
    public void initBloomFilter(long expectedInsertions, double falseProbability) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_NAME);
        bloomFilter.tryInit(expectedInsertions, falseProbability);
    }

    // 添加元素到布隆过滤器中
    public void addToBloomFilter(String key) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_NAME);
        bloomFilter.add(key);
    }

    // 检查元素是否存在于布隆过滤器中
    public boolean mightContain(String key) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_NAME);
        return bloomFilter.contains(key);
    }
}

代码解释

  1. 初始化布隆过滤器initBloomFilter 方法可以根据期望插入的数量和误判率初始化布隆过滤器。布隆过滤器的大小和哈希函数数量根据这些参数自动计算。
  2. 添加元素addToBloomFilter 方法向布隆过滤器中添加新的 key。
  3. 判断元素是否存在mightContain 方法用来判断某个 key 是否在布隆过滤器中。

4. 布隆过滤器的初始化

通常我们会在应用启动时初始化布隆过滤器,并将数据库中的所有 key 预先加入过滤器。

package com.coderjia.boot310redis.config;

import com.coderjia.boot310redis.bean.Product;
import com.coderjia.boot310redis.dao.ProductMapper;
import com.coderjia.boot310redis.service.BloomFilterService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author CoderJia
 * @create 2024/10/6 下午 12:38
 * @Description
 **/
@Slf4j
@Component
public class BloomFilterInitializer implements CommandLineRunner {

    @Autowired
    private BloomFilterService bloomFilterService;

    @Autowired
    private ProductMapper productMapper;

    @Override
    public void run(String... args) throws Exception {

        // 查询所有产品数据
        List<Product> all = productMapper.findAll();

        // 初始化布隆过滤器
        bloomFilterService.initBloomFilter(all.size(), 0.01);

        // 将所有产品的ID加入布隆过滤器
        all.forEach(product -> {
            bloomFilterService.addToBloomFilter(product.getId().toString());
        });

        log.info("初始化布隆过滤器完成,添加产品数:{}", all.size());
    }
}

代码解释

  • 在应用启动时,通过 CommandLineRunner 初始化布隆过滤器,并将数据库中的所有商品 ID 加入过滤器中。

5. 使用布隆过滤器进行缓存穿透防护

接下来,我们通过一个简单的示例,结合 Redis 的缓存功能和布隆过滤器,展示如何防止缓存穿透。

package com.coderjia.boot310redis.service;

import com.coderjia.boot310redis.bean.Product;
import com.coderjia.boot310redis.dao.ProductMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

/**
 * @author CoderJia
 * @create 2024/10/6 下午 12:37
 * @Description
 **/
@Slf4j
@Service
public class ProductService {

    @Autowired
    private BloomFilterService bloomFilterService;

    @Autowired
    private ProductMapper productMapper;

    @Cacheable(value = "product", key = "#p0")
    public Product getProductById(Long id) {
        // 使用布隆过滤器防止缓存穿透
        if (!bloomFilterService.mightContain(id.toString())) {
            throw new IllegalArgumentException("Product not found!");
        }

        log.info("准备查询产品信息,id:{}", id);
        return productMapper.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Product not found!"));
    }
}

代码解释

  1. 在查询商品前,首先通过布隆过滤器判断 key 是否可能存在。若布隆过滤器判断 key 不存在,则直接抛出异常,避免查询数据库。
  2. 如果布隆过滤器判断 key 可能存在,接着通过缓存获取商品数据。如果缓存未命中,则查询数据库。

productMapper 参考:

package com.coderjia.boot310redis.dao;

import com.coderjia.boot310redis.bean.Product;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
import java.util.Optional;

/**
 * @author CoderJia
 * @create 2024/3/16 下午 05:22
 * @Description
 **/
@Mapper
public interface ProductMapper {

    Optional<Product> findById(Long id);

    List<Product> findAll();
}

Product 也很简单:

package com.coderjia.boot310redis.bean;

import lombok.Data;

import java.io.Serial;
import java.io.Serializable;

/**
 * @author CoderJia
 * @create 2024/10/6 下午 12:46
 * @Description
 **/
@Data
public class Product implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    private Long id;
    private String name;
}

6. 测试效果

在你的业务逻辑中调用上面创建的 getProductById 方法。

package com.coderjia.boot310redis.demos.web;

import com.coderjia.boot310redis.bean.Product;
import com.coderjia.boot310redis.service.LockService;
import com.coderjia.boot310redis.service.ProductService;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author CoderJia
 * @create 2024/10/5 下午 05:14
 * @Description
 **/
@Slf4j
@RestController
public class LockController {

    @Autowired
    private ProductService productService;

    @GetMapping("/get-product")
    public Product getProduct(@RequestParam("id") Long id) {
        log.info("准备产品产品信息,id:{}", id);
        try {
            Product product = productService.getProductById(id);
            return product;
        }catch (Exception e) {
            log.error("获取产品信息异常,id:{}", id, e);
            return null;
        }
    }
}

6.1. 启动项目

可以看到布隆过滤器的初始化过程,查询出所有的产品信息并添加到布隆过滤器中。

image-20241006190759822

6.2. 查询存在的商品

调用 curl "http://localhost:8080/get-product?id=1" 接口:

image-20241006192152018

6.3. 查询不存在的商品

调用 curl "http://localhost:8080/get-product?id=101" 接口,产品不存在布隆过滤器器中,直接报错。

image-20241006192227320

7. 总结

通过结合 Spring Boot 3RedisRedisson,我们可以非常方便地实现布隆过滤器,来防止缓存穿透问题。在高并发场景下,布隆过滤器是一种有效的工具,可以降低数据库的压力,提升系统性能。布隆过滤器并不是万能的,在某些场景下会有少量误判,但结合 Redis 的强大功能,它依然是防止缓存穿透的最佳选择之一。

关键点总结

  • 布隆过滤器通过空间换时间,能够快速判断元素是否存在,减少无效请求。
  • Redisson 提供了开箱即用的布隆过滤器 API,大大简化了开发工作。
  • 在结合缓存时,布隆过滤器可以显著减少数据库查询,提升系统性能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

netdata保姆级面板介绍

netdata保姆级面板介绍 基本介绍部署流程下载安装指令选择设置KSM为什么要启用 KSM&#xff1f;如何启用 KSM&#xff1f;验证 KSM 是否启用注意事项 检查端口启动状态 netdata和grafana的区别NetdataGrafananetdata各指标介绍总览system overview栏仪表盘1. CPU2. Load3. Disk…

NUKE 15有哪些新的改进功能?影视后期特效合成NUKE 15 安装包分享 【Mac/win】

Nuke 15是一款由英国The Foundry公司开发的专业的合成软件&#xff0c;被广泛用于电影、电视和广告制作中的后期合成和特效制作。 Nuke 15拥有强大的功能和灵活性&#xff0c;可以帮助用户处理各种复杂的合成任务&#xff0c;包括图像修复、色彩校正以及粒子特效等。它具备高效…

Spring Validation —— 参数校验框架

案例说明——后端校验注册表单字段 在编写注册功能时&#xff0c;需要考虑字段校验的情况&#xff0c;这时候可以采用 Spring提供的一套参数校验框架工具——Spring Validation。一下是使用的步骤&#xff1a; 1. 导入validation坐标 2. 在参数上添加 Pattern注解&#xff0c…

尚硅谷javaSpring

尚硅谷课件: 分类&#xff1a;尚硅谷Spring6教程 - Lixx Blog - 李晓旭的博客 简介 Java Spring 是一个开源的、全面的企业级应用开发框架&#xff0c;旨在简化企业级应用的开发。Spring 框架最初由 Rod Johnson 在 2002 年发布&#xff0c;并随着时间的推移&#xff0c;它已…

【源码+文档】基于Java的新能源停车场管理系统的设计与实现

&#x1f6a9;如何选题&#xff1f; 如何选题、让题目的难度在可控范围&#xff0c;以及如何在选题过程以及整个毕设过程中如何与老师沟通&#xff0c;这些问题是需要大家在选题前需要考虑的&#xff0c;具体的方法我会在文末详细为你解答。 &#x1f6ad;如何快速熟悉一个项…

低质量数据的多模态融合方法

目录 多模态融合 低质量多模态融合的核心挑战 噪声多模态数据学习 缺失模态插补 平衡多模态融合 动态多模态融合 启发式动态融合 基于注意力的动态融合 不确定性感知动态融合 论文 多模态融合 多模态融合侧重于整合多种模态的信息,以实现更准确的预测,在自动驾驶、…

【小沐学GIS】blender导入OpenTopography地形数据(BlenderGIS、OSM、Python)

文章目录 1、简介1.1 blender1.2 OpenStreetMap地图 2、BlenderGIS2.1 下载BlenderGIS2.2 安装BlenderGIS2.3 申请opentopography的key2.4 抓取卫星地图2.5 生成高度图2.6 获取OSM数据 结语 1、简介 1.1 blender https://www.blender.org/ Blender 是一款免费的开源 3D 创作套…

【c++】初步了解类和对象2

1、类的作用域 类定义了一个新的作用域&#xff0c;类的所有成员都在类的作用域中。在类体外定义成员时&#xff0c;需要使用 :: 作用域操作符指明成员属于哪个类域。 如图&#xff0c;此时在类内声明了函数firstUniqChar()&#xff0c;在类外进行了函数体的具体定义。 但是却…

使用 classification_report 评估 scikit-learn 中的分类模型

介绍 在机器学习领域&#xff0c;评估分类模型的性能至关重要。scikit-learn 是一个功能强大的 Python 机器学习工具&#xff0c;提供了多种模型评估工具。其中最有用的函数之一是 classification_report&#xff0c;它可以全面概述分类模型的关键指标。在这篇文章中&#xff…

国庆作业

day1 1.开发环境 Linux系统GCCFDBmakefilesqlite3 2.功能描述 项目功能: 服务器&#xff1a;处理客户端的请求&#xff0c;并将数据存入数据库中&#xff0c;客户端请求的数据从数据库进行获取&#xff0c;服务器转发给客户端。 用户客户端&#xff1a;实现账号的注册、登…

加密软件有哪些?2024年十大好用的企业文件加密软件大盘点

随着数字化转型的加速&#xff0c;企业面临的数据安全威胁日益增加。为防止敏感数据泄露&#xff0c;企业文件加密已成为保护公司机密信息的必要手段。以下是2024年十大好用的企业文件加密软件大盘点&#xff0c;帮助企业在复杂的数字环境中确保数据安全。 1.安秉加密软件 安秉…

Navicat下载安装

官网地址&#xff1a;Navicat | Download Navicat Premium 14-day trial versions for Windows, macOS and Linux 1、进入官网下载地址&#xff0c;根据需求进行下载 2、双击安装程序&#xff0c;点击【下一步】 3、选择【我同意】&#xff0c;点击下一步 4、自定义安装路径&a…

基于Dify的工作流简单测试

文章目录 工作流定义工作流构建新建工作流任务分解任务分类任务执行日常聊天任务执行计算字符串长度的三次幂任务执行获取ip地址任务执行其他任务不执行 变量汇集结果返回效果展示 工作流定义 下面是工作流官方文档中给出的工作流定义&#xff0c;其实工作流与Agent调用的对象…

java:pdfbox 3.0 去除扫描版PDF中文本水印

官网下载 https://pdfbox.apache.org/download.html下载 pdfbox-app-3.0.3.jar cd D:\pdfbox 运行 java -jar pdfbox-app-3.0.3.jar java -jar pdfbox-app-3.0.3.jar Usage: pdfbox [COMMAND] [OPTIONS] Commands:debug Analyzes and inspects the internal structu…

《Windows PE》4.3 延迟加载导入表

延迟加载导入表&#xff08;Delayed Import Table&#xff09;是PE文件中的一个数据结构&#xff0c;用于实现延迟加载&#xff08;Lazy Loading&#xff09;外部函数的机制。 延迟加载是指在程序运行时&#xff0c;只有当需要使用某个外部函数时才进行加载和绑定&#xff0c;…

Llama系列上新多模态!3.2版本开源超闭源,还和Arm联手搞了手机优化版,Meta首款多模态Llama 3.2开源!1B羊驼宝宝,跑在手机上了

Llama系列上新多模态&#xff01;3.2版本开源超闭源&#xff0c;还和Arm联手搞了手机优化版&#xff0c;Meta首款多模态Llama 3.2开源&#xff01;1B羊驼宝宝&#xff0c;跑在手机上了&#xff01; 在多模态领域&#xff0c;开源模型也超闭源了&#xff01; 就在刚刚结束的Met…

VSCode运行QT界面

VSCode用久了,感觉Qt Creator的写起代码来还是不如VSCode得心应手,虽然目前还是存在一些问题,先把目前实现的状况做个记录,后续有机会再进一步优化。 当前方式 通过QtCreator创建一个CMake项目,然后使用CMake的方式在VSCode中进行编译。 claude给出的建议 左上角的名字会…

C++ 算法学习——1.6 前缀和与二维前缀和算法

前缀和算法&#xff08;Prefix Sum Algorithm&#xff09;&#xff1a; 概念&#xff1a;前缀和算法通过在遍历数组时计算前缀和&#xff08;从数组的第一个元素开始累加到当前元素的和&#xff09;&#xff0c;可以在O(1)时间内得到任意区间的子数组和&#xff0c;而不需要重复…

详解 PDF 转 JPG:简单操作,高效转换

如今&#xff0c;众多软件都已具备将PDF转换为JPG的功能&#xff0c;所以pdf怎么转换成jpg图片已经不难解决了吧。接下来&#xff0c;我想分享几款依然保存在我电脑中&#xff0c;且非常实用的PDF转JPG工具给大家。 1.福昕PDF转换大师 链接一下>>https://www.pdf365.cn…

【2024年10月测试通过】conda下使用虚拟环境安装最新版pytorch2.4+cuda12.4

开头先说重点&#xff1a; 1.采用conda的虚拟环境&#xff0c;会在沙盒环境下安装好所有所需包&#xff0c;而且该虚拟环境拷贝给其他人员可以直接用&#xff0c;很方便。 2.pytorch官网访问不了&#xff0c;有一个国内镜像推荐&#xff0c;地址为PyTorch - PyTorch 中文 3.…