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

news2024/10/7 11:32:56

更多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.coloradmin.cn/o/2194064.html

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

相关文章

Spring开发最佳实践之跨域处理

1. 跨域处理 1.1 异常现象 1.2 异常原因分析 跨源资源共享的官方定义如下&#xff1a; 跨源资源共享&#xff08;CORS&#xff0c;Cross Origin Resource Sharing。或通俗地译为跨域资源共享&#xff09;是一种基于 HTTP 头的机制&#xff0c;该机制通过允许服务器标示除了它自…

上海理工大学《2023年+2019年867自动控制原理真题》 (完整版)

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;《上海理工大学867自控考研资料》的真题篇。后续会持续更新更多学校&#xff0c;更多年份的真题&#xff0c;记得关注哦~ 目录 2023年真题 2019年真题 Part1&#xff1a;2023年2019年完整版真题 2023年真题 2019年…

Java使用线程池创建线程

一、线程前言 首先我们知道&#xff0c;线程的概念如果不知道可以去看这一篇Java中的线程&#xff0c;我们这篇主要讲述的是Java怎么使用线程池创建线程&#xff0c;首先我们要对线程池有点概念&#xff0c;其实顾名思义&#xff0c;线程池就是有喝多线程的一个池子类似于&…

一书讲透LLM大语言模型,《掌握大型语言模型》,看完我都懵了!

《掌握大型语言模型》 &#xff08;Mastering Large Language Models&#xff09;由Sanket Subhash Khandare撰写&#xff0c;是一本关于大型语言模型&#xff08;LLMs&#xff09;的高级技术、应用、前沿方法和顶尖模型的指南。 这本大模型书已经上传CSDN&#xff0c;朋友们如…

《Windows PE》4.2 绑定导入表

绑定导入表&#xff08;Bound Import Table&#xff09;是文件中的一个数据结构&#xff0c;用于存储已经绑定&#xff08;即完成绑定导入&#xff09;的外部函数的信息。 本节必须掌握的知识点&#xff1a; 绑定导入表数据结构 实例分析 4.2.1 绑定导入表数据结构 绑定导入…

【AIGC】ChatGPT是如何思考的:探索CoT思维链技术的奥秘

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;什么是CoT思维链CoT思维链的背景与技术发展需求 &#x1f4af;CoT思维链的工作原理&#x1f4af;CoT思维链的应用领域&#x1f4af;CoT思维链的优势&#x1f4af;CoT思维…

动态内存管理笔试题

目录 1.第一题1.1如何修改 2.第二题2.1题想2.2深刻理解 3.第三题4.第四题 1.第一题 void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) {char* str NULL;GetMemory(str);strcpy(str, "hello world");printf(str); }请问运⾏Test 函数会有什么样的…

解锁数字化营销成功密码

在趋势部分&#xff0c;列举了移动优先、社交媒体主导、个性化营销、视频营销崛起和数据驱动决策等方面&#xff0c;让读者快速了解数字化营销的发展方向。策略部分强调了明确目标受众、制定整合营销策略、优化用户体验、重视内容营销和社交媒体营销以及利用搜索引擎优化和数据…

jQuery——平滑翻页

平滑翻页 param next true&#xff1a;下一页 false&#xff1a;下一页 本文分享到此结束&#xff0c;欢迎大家评论区相互讨论学习&#xff0c;下一篇继续分享jQuery中循环翻页的学习。

Docker 实践与应用举例

Docker 实践与应用举例 Docker 已经成为现代软件开发和部署中的重要工具&#xff0c;通过容器化技术&#xff0c;开发者可以轻松管理应用的依赖环境、简化部署流程&#xff0c;并实现跨平台兼容性。本篇博客将详细介绍 Docker 的基本概念、实践操作以及应用场景&#xff0c;帮…

工业缺陷检测深度学习方法

工业缺陷检测深度学习方法 基于深度学习的工业缺陷检测方法可以降低传统人工质检的成本, 提升检测的准确性与效率, 因而在智能制造中扮演重要角色, 并逐渐成为计算机视觉领域新兴的研究热点之一. 其被广泛地应用 于无人质检、智能巡检、质量控制等各种生产与运维场景中. 本综述…

跨设备剪贴板同步服务ClipCascade

什么是 ClipCascade &#xff1f; ClipCascade 是一款开源的轻量级工具&#xff0c;可以自动同步您的剪贴板在多个设备之间&#xff0c;无需按键。它确保设备之间无缝的剪贴板共享&#xff0c;并以端对端加密优先保护隐私。无论您是在不同工作站之间切换&#xff0c;还是仅仅希…

检索增强思考 RAT(RAG+COT):提升 AI 推理能力的强大组合

在人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;已经取得了显著的进展&#xff0c;能够生成类似人类的文本并回答各种问题。然而&#xff0c;它们在推理过程中仍面临一些挑战&#xff0c;例如缺乏对事实的准确把握以及难以处理复杂的多步骤问题。为了解决…

Unity3D 单例模式

Unity3D 泛型单例 单例模式 单例模式是一种创建型设计模式&#xff0c;能够保证一个类只有一个实例&#xff0c;提供访问实例的全局节点。 通常会把一些管理类设置成单例&#xff0c;例如 GameManager、UIManager 等&#xff0c;可以很方便地使用这些管理类单例&#xff0c;…

用YOLO和LLM增强的OCR

虽然最近我花了很多时间在大型语言模型 (LLM) 上进行实验&#xff0c;但我对计算机视觉的热情始终未减。因此&#xff0c;当我有机会将两者融合在一起时&#xff0c;我迫不及待地想要立即开始。在 Goodreads 上扫描书籍封面并将其标记为已读一直感觉有点神奇&#xff0c;我很兴…

SSM外卖点餐软件APP-计算机毕业设计源码30768

目 录 摘要 1 绪论 1.1 研究背景 1.2研究目的 1.3论文结构与章节安排 2 外卖点餐软件APP系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能…

这些编程工具竟然能让我效率翻倍?开发者必备神器盘点!

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

基于深度学习多层感知机进行手机价格预测

数据集介绍 数据集采用了Kaggle实战数据集,链接如下,如有需要可自行下载 https://www.kaggle.com/datasets/atefehmirnaseri/cell-phone-price/data 数据集简要介绍 • battery_power&#xff1a;电池的总能量存储&#xff08;毫安时&#xff09; • blue&#xff1a;设备…

人工智能对未来工作影响的四种可能性

随着人工智能&#xff08;AI&#xff09;技术的迅速发展&#xff0c;其对人类工作的影响已成为讨论的热点话题。我们经常听到有关AI威胁论的观点&#xff0c;担心它将取代人类工作&#xff0c;但也有专家认为AI将成为一种辅助工具&#xff0c;帮助人类提升工作效率。宾夕法尼亚…

嵌入式硬件设计

嵌入式硬件设计是指针对嵌入式系统&#xff08;一种专用的计算机系统&#xff0c;通常嵌入到其他设备中&#xff09;进行的硬件设计工作。嵌入式系统广泛应用于消费电子、工业控制、医疗设备、汽车电子、航空航天等领域。以下是嵌入式硬件设计的主要内容和步骤&#xff1a; 1.…