Spring Boot 使用 Redis 实现布隆过滤器,精准过滤 “无效” 请求
在当今高并发的互联网应用场景下,如何高效地对海量数据进行过滤,避免无效请求对系统资源造成浪费,是每一个开发者都需要面对的问题。布隆过滤器(Bloom Filter)作为一种空间效率极高的概率型数据结构,为我们提供了出色的解决方案。而结合 Spring Boot 的便捷开发特性与 Redis 的高性能存储,能够轻松地将布隆过滤器应用到实际项目中,让我们的系统性能得到质的飞跃。接下来,就详细介绍如何在 Spring Boot 项目中利用 Redis 实现布隆过滤器。
一、布隆过滤器简介
布隆过滤器由 Burton Howard Bloom 在 1970 年提出,它本质上是一个长度为 m
的位数组,初始值全部为 0。同时,它拥有 k
个相互独立的哈希函数,这些哈希函数能将任意输入元素均匀地映射到 m
个位置上。
当向布隆过滤器中添加一个元素时,先使用 k
个哈希函数分别对该元素进行计算,得到 k
个哈希值,然后将位数组中对应这 k
个哈希值的位置置为 1。判断一个元素是否在布隆过滤器中时,同样用 k
个哈希函数计算得到 k
个哈希值,查看位数组中对应位置是否都为 1。若有一个位置为 0,则该元素一定不在;若全部为 1,则该元素很可能在(存在一定的误判概率,这是由于哈希碰撞导致的,但可以通过合理设置参数来控制在极低水平)。
正是这种巧妙的设计,使得布隆过滤器在存储海量数据时,占用空间极小,并且查询效率极高,能够快速判断元素的 “大致存在性”,特别适用于如缓存穿透场景下的无效请求过滤、大规模数据去重等任务。
二、准备工作:Spring Boot 与 Redis 集成
- 首先,在 Spring Boot 项目的
pom.xml
文件中引入必要的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这里引入了 Spring Boot 与 Redis 集成的基础依赖以及用于构建 Web 应用的依赖,确保项目既能与 Redis 交互,又具备对外提供服务的能力。
- 在
application.properties
或application.yml
配置文件中配置 Redis 连接信息:
spring:
redis:
host: localhost
port: 6379
password: your_password # 如果 Redis 设置了密码,填写此处
database: 0
根据实际使用的 Redis 服务器情况,调整主机名、端口号、密码以及数据库编号等参数,确保 Spring Boot 项目能够顺利连接到 Redis。
三、代码实现:构建基于 Redis 的布隆过滤器
- 创建布隆过滤器配置类:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.sentinel.SentinelConfiguration;
import org.springframework.data.redis.sentinel.SentinelConnectionFactory;
import org.springframework.data.redis.sentinel.SentinelRedisTemplate;
import com.google.common.hash.Funnel;
import com.google.common.hash.Hashing;
import io.rebloom.client.Client;
@Configuration
public class BloomFilterConfig {
// 定义布隆过滤器的哈希函数个数
private static final int NUM_HASH_FUNCTIONS = 5;
// 定义布隆过滤器的位数组大小
private static final int BIT_ARRAY_SIZE = 1000000;
@Bean
public Client redisBloomFilterClient(RedisConnectionFactory redisConnectionFactory) {
return new Client(redisConnectionFactory, "bloom");
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
@Bean
public Funnel<CharSequence> funnel() {
return (Funnel<CharSequence>) (from, into) -> into.putString(from, Charsets.UTF_8).writeBytes();
}
@Bean
public io.rebloom.client.BloomFilter bloomFilter(Client redisBloomFilterClient, Funnel<CharSequence> funnel) {
io.rebloom.client.BloomFilter bloomFilter = redisBloomFilterClient.createFilter("myBloomFilter", BIT_ARRAY_SIZE, NUM_HASH_FUNCTIONS, funnel);
return bloomFilter;
}
}
在这个配置类中,我们首先定义了布隆过滤器的关键参数:哈希函数个数和位数组大小,这两个参数需要根据实际业务需求和数据量进行合理调整。然后,通过 Spring 的 @Configuration
和 @Bean
注解,创建了与 Redis 交互的 Client
对象(用于操作 Redis 中的布隆过滤器)、通用的 RedisTemplate
对象(方便后续其他 Redis 操作)以及构建布隆过滤器所需的 Funnel
对象和 io.rebloom.client.BloomFilter
对象。
- 编写业务逻辑类,利用布隆过滤器进行数据过滤:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import io.rebloom.client.BloomFilter;
@RestController
public class BloomFilterController {
@Autowired
private BloomFilter bloomFilter;
@GetMapping("/check")
public String checkData(String data) {
boolean mightContain = bloomFilter.mightContain(data);
if (!mightContain) {
// 如果布隆过滤器判断数据不存在,直接返回,避免后续昂贵的数据库查询等操作
return "Data not likely to be in the set";
}
// 此处可添加进一步的精确查询逻辑,如数据库查询,因为布隆过滤器存在误判可能
// 假设这里经过精确查询发现数据确实存在
return "Data is in the set";
}
@GetMapping("/add")
public String addData(String data) {
bloomFilter.add(data);
return "Data added successfully";
}
}
在这个 RestController
类中,我们注入了之前配置好的布隆过滤器。checkData
方法用于接收外部传入的数据,通过布隆过滤器快速判断该数据是否可能存在于集合中,如果判断为不存在,直接返回提示信息,避免不必要的后续操作,节省系统资源;如果判断为可能存在,则可以根据实际业务需求,进行进一步的精确查询(如查询数据库),因为布隆过滤器存在一定误判概率。addData
方法则用于向布隆过滤器中添加数据,以便后续进行过滤判断。
四、测试与验证
启动 Spring Boot 项目后,我们可以使用工具如 Postman 对编写的接口进行测试。
- 首先,通过
http://localhost:8080/add
接口添加一些数据到布隆过滤器中,例如发送POST
请求,请求体中包含数据“user1”
、“productA”
等,多次添加不同的数据,模拟实际业务场景下数据的录入过程。 - 然后,使用
http://localhost:8080/check
接口来测试过滤效果,发送GET
请求,查询刚刚添加的数据以及一些未添加的数据。对于已添加的数据,布隆过滤器大概率会判断其存在(由于误判,偶尔可能会判断为不存在,但概率极低);对于未添加的数据,布隆过滤器几乎肯定会判断其不存在,从而有效拦截了无效请求,验证了布隆过滤器的功能。
通过以上步骤,我们成功在 Spring Boot 项目中利用 Redis 实现了布隆过滤器,实现了对数据的高效过滤,提升了系统在高并发场景下的应对能力。在实际应用中,还可以根据项目需求进一步优化布隆过滤器的参数设置、扩展功能,让它更好地服务于我们的系统。希望这篇文章能帮助你掌握这一实用技术,让你的开发之路更加顺畅!
需要注意的是,文中所使用的 io.rebloom.client
相关类来自于 Redis 的 Rebloom 模块扩展,在使用前确保已经正确安装并引入相关依赖。同时,在优化布隆过滤器参数时,可以参考一些数学公式和实际经验,如根据预估的数据集大小和可接受的误判率来精确计算哈希函数个数和位数组大小,以达到最佳的性能平衡。