重学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 {@Autowiredprivate RedissonProperties redissonProperties;@Beanpublic 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";@Autowiredprivate 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 {@Autowiredprivate BloomFilterService bloomFilterService;@Autowiredprivate ProductMapper productMapper;@Overridepublic 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 {@Autowiredprivate BloomFilterService bloomFilterService;@Autowiredprivate 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 {@Serialprivate 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 {@Autowiredprivate 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.mzph.cn/web/54570.shtml

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

相关文章

【python实操】python小程序之对象的属性操作

引言 python小程序之对象的属性操作 文章目录 引言一、对象的属性操作1.1 题目1.2 代码1.3 代码解释 二、思考2.1 添加属性2.2 获取属性 一、对象的属性操作 1.1 题目 给对象添加属性 1.2 代码 class Cat:# 在缩进中书写⽅法def eat(self):# self 会⾃动出现,暂不管print(f…

【前端开发入门】前端开发环境配置

目录 引言一、Vscode编辑器安装1. 软件下载2. 软件安装3. 插件安装 二、Nodejs环境安装及版本控制1. 安装内容2. 使用nvm安装2.1 软件下载并安装2.2 nvm基本指令2.3 nvm下载过慢导致超时解决 三、git安装及配置1. 软件下载2. 软件安装3. 基础配置 四、总结 引言 本系列教程旨在…

知识图谱入门——5:Neo4j Desktop安装和使用手册(小白向:Cypher 查询语言:逐步教程!Neo4j 优缺点分析)

Neo4j简介 Neo4j 是一个基于图结构的 NoSQL 数据库&#xff0c;专门用于存储、查询和管理图形数据。它的核心思想是使用节点、关系和属性来描述数据。图数据库非常适合那些需要处理复杂关系的数据集&#xff0c;如社交网络、推荐系统、知识图谱等领域。 与传统的关系型数据库…

【韩顺平Java笔记】第7章:面向对象编程(基础部分)【227-261】

文章目录 227. 重载介绍228. 重载快速入门229. 重载使用细节230. 重载课堂练习1231. 232. 重载课堂练习2,3233. 可变参数使用233.1 基本概念233.2 基本语法233.3 快速入门案例 234. 可变参数细节235. 可变参数练习236. 作用域基本使用237. 作用域使用细节1238. 作用域使用细节2…

基于FPGA的ov5640摄像头图像采集(二)

之前讲过ov5640摄像头图像采集&#xff0c;但是只包了的摄像头驱动与数据对齐两部分&#xff0c;但是由于摄像头输入的像素时钟与HDMI输出的驱动时钟并不相同&#xff0c;所有需要利用DDR3来将像素数据进行缓存再将像素数据从DDR3中读出&#xff0c;对DDR3的读写参考米联客的IP…

Hallo部署指南

一、介绍 Hallo是由复旦大学、百度公司、苏黎世联邦理工学院和南京大学的研究人员共同提出的一个AI对口型肖像图像动画技术&#xff0c;可基于语音音频输入来驱动生成逼真且动态的肖像图像视频。 该框架采用了基于扩散的生成模型和分层音频驱动视觉合成模块&#xff0c;提高了…

独立站如何批量查收录,独立站批量查询收录的操作方法

独立站批量查询收录是SEO优化过程中的一项重要任务&#xff0c;它有助于网站管理员全面了解网站在搜索引擎中的表现情况。以下是一些常用的独立站批量查询收录的操作方法&#xff1a; 一、使用搜索引擎的Site指令结合自动化工具 编写脚本或配置爬虫&#xff1a; 利用Python、…

【Flutter】- 核心语法

文章目录 知识回顾前言源码分析1. 有状态组件2. 无状态组件3. 组件生命周期4. 常用组件Container组件Text组件Image组件布局组件row colum stack expandedElevntButton按钮拓展知识总结知识回顾 【Flutter】- 基础语法 前言 Flutter是以组件化的思想构建客户端页面的,类似于…

windows C++-创建数据流代理(二)

完整的数据流演示 下图显示了 dataflow_agent 类的完整数据流网络&#xff1a; 由于 run 方法是在一个单独的线程上调用的&#xff0c;因此在完全连接网络之前&#xff0c;其他线程可以将消息发送到网络。 _source 数据成员是一个 unbounded_buffer 对象&#xff0c;用于缓冲…

网站建设中常见的网站后台开发语言有哪几种,各自优缺点都是什么?

市场上常见的网站后台开发语言有PHP、Python、JavaScript、Ruby、Java和.NET等。这些语言各有其独特的优缺点&#xff0c;适用于不同的开发场景和需求。以下是对这些语言的具体介绍&#xff1a; PHP 优点&#xff1a;PHP是一种广泛用于Web开发的动态脚本语言&#xff0c;特别适…

Diffusion models(扩散模型) 是怎么工作的

前言 给一个提示词, Midjourney, Stable Diffusion 和 DALL-E 可以生成很好看的图片&#xff0c;那么它们是怎么工作的呢&#xff1f;它们都用了 Diffusion models&#xff08;扩散模型&#xff09; 这项技术。 Diffusion models 正在成为生命科学等领域的一项尖端技术&…

基于STM32的智能花盆浇水系统设计

引言 本项目设计了一个基于STM32的智能花盆浇水系统。该系统通过土壤湿度传感器检测土壤湿度&#xff0c;当湿度低于设定阈值时&#xff0c;自动启动水泵进行浇水。它还结合了温湿度传感器用于环境监测。该项目展示了STM32在传感器集成、自动控制和节水智能化应用中的作用。 …

Nginx05-基础配置案例

零、文章目录 Nginx05-基础配置案例 1、案例需求 &#xff08;1&#xff09;有如下访问 http://192.168.119.161:8081/server1/location1 访问的是&#xff1a;index_sr1_location1.htmlhttp://192.168.119.161:8081/server1/location2 访问的是&#xff1a;index_sr1_loca…

YoloV9改进策略:BackBone改进|CAFormer在YoloV9中的创新应用,显著提升目标检测性能

摘要 在目标检测领域,模型性能的提升一直是研究者和开发者们关注的重点。近期,我们尝试将CAFormer模块引入YoloV9模型中,以替换其原有的主干网络,这一创新性的改进带来了显著的性能提升。 CAFormer,作为MetaFormer框架下的一个变体,结合了深度可分离卷积和普通自注意力…

Ansible学习之ansible-pull命令

想要知道ansible-pull是用来做什么的&#xff0c;就需要了解Ansible的工作模&#xff0c;Ansible的工作模式有两种&#xff1a; push模式 push推送&#xff0c;这是Ansible的默认模式&#xff0c;在主控机上编排好playbook文件&#xff0c;push到远程主机上来执行。pull模式 p…

远程调用的问题以及eureka原理

目录 服务调用出现的问题 问题分析 解决方案&#xff08;eureka原理&#xff09; eureka&#xff08;两个角色&#xff09; eureka的解决方案 此过程出现的问题 eureka的作用 总结 服务调用出现的问题 服务消费者该如何获取服务提供者的地址信息&#xff1f;如果有多个…

系统架构设计师论文《论企业应用系统的数据持久层架构设计》精选试读

论文真题 数据持久层&#xff08;Data Persistence Layer&#xff09;通常位于企业应用系统的业务逻辑层和数据源层之间&#xff0c;为整个项目提供一个高层、统一、安全、并发的数据持久机制&#xff0c;完成对各种数据进行持久化的编程工作&#xff0c;并为系统业务逻辑层提…

【SpringBoot】基础+JSR303数据校验

目录 一、Spring Boot概要 1. SpringBoot介绍 2. SpringBoot优点 3. SpringBoot缺点 4. 时代背景-微服务 二、Spring Boot 核心配置 1. Spring Boot配置文件分类 1.1 application.properties 1.2 application.yml 1.3 小结 2. YAML概述 3. YAML基础语法 3.1 注意事…

【教程】57帧! Mac电脑流畅运行黑神话悟空

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、先安装CrossOver。网上有许多和谐版&#xff0c;可自行搜索。&#xff08;pd虚拟机里运行黑神话估计够呛的&#xff09; 2、运行CrossOver&#xf…

SpringBoot上传图片实现本地存储以及实现直接上传阿里云OSS

一、本地上传 概念&#xff1a;将前端上传的文件保存到自己的电脑 作用&#xff1a;前端上传的文件到后端&#xff0c;后端存储的是一个临时文件&#xff0c;方法执行完毕会消失&#xff0c;把临时文件存储到本地硬盘中。 1、导入文件上传的依赖 <dependency><grou…