40人参与 • 2026-02-02 • Redis
在缓存架构中,总有一些“头疼问题”:用户反复提交相同请求、查询不存在的key导致缓存穿透、海量数据去重效率低下……这些场景下,redis布隆过滤器就是当之无愧的“救星”。它像一个智能门卫,能快速判断“这个人是不是来过”“这个key是不是不存在”,用极小的空间成本实现高效过滤,性能远超传统的数据库查询或全量缓存校验。
今天咱们就从“是什么、为什么好用、怎么用redis快速实现”三个维度,用通俗的语言+实操代码,把布隆过滤器讲透。全程避开复杂公式,就算是刚接触缓存的同学,也能跟着步骤快速落地。
布隆过滤器(bloom filter)本质是一个基于哈希函数的概率型数据结构,核心作用是“快速判断一个元素是否存在于集合中”。它不像哈希表那样存储完整数据,而是用一个二进制数组(bit数组)+多个哈希函数,通过标记元素的哈希位置来实现过滤。
咱们用“小区门卫记访客”的场景类比,秒懂核心逻辑:
布隆过滤器的优势和局限性都很鲜明,落地前必须摸清:
redis本身没有内置布隆过滤器,但提供了两种快速实现的方案:一是基于redis的bitmap(位图)手动实现,灵活可控;二是使用redis官方推荐的redisson客户端,封装好现成api,开箱即用。咱们分别讲实操,按需选择即可。
核心思路:利用redis的bitmap数据结构作为布隆过滤器的bit数组,通过多个哈希函数计算元素的哈希值,将对应位置的bit置为1;查询时,同样计算哈希值,检查所有位置是否为1,全为1则大概率存在,否则一定不存在。
手动实现前,需先确定三个核心参数,可通过公式或在线工具计算:
常用计算公式(无需死记,在线工具直接算):
举个例子:预估存储10万条数据,误判率设为0.01,计算得m≈958505 bit(约117kb),k≈7个哈希函数。
核心是实现多个哈希函数,操作redis的bitmap指令(setbit置1,getbit查询):
import org.springframework.data.redis.core.stringredistemplate;
import java.nio.charset.standardcharsets;
import java.security.messagedigest;
import java.security.nosuchalgorithmexception;
/**
* 基于redis bitmap手动实现布隆过滤器
*/
public class redisbloomfilter {
// redis键名
private final string key;
// bit数组长度
private final long bitsize;
// 哈希函数个数
private final int hashcount;
private final stringredistemplate stringredistemplate;
// 构造器:初始化参数
public redisbloomfilter(string key, long n, double p, stringredistemplate stringredistemplate) {
this.key = key;
this.stringredistemplate = stringredistemplate;
// 计算bit数组长度
this.bitsize = (long) (-n * math.log(p) / (math.log(2) * math.log(2)));
// 计算哈希函数个数
this.hashcount = (int) (this.bitsize / n * math.log(2));
}
// 添加元素到布隆过滤器
public void add(object value) {
byte[] bytes = value.tostring().getbytes(standardcharsets.utf_8);
long[] hashes = hash(bytes, hashcount, bitsize);
for (long hash : hashes) {
// 把对应bit位置置为1
stringredistemplate.opsforvalue().setbit(key, hash, true);
}
}
// 判断元素是否存在(存在返回true,不存在返回false;true可能是误判)
public boolean contains(object value) {
byte[] bytes = value.tostring().getbytes(standardcharsets.utf_8);
long[] hashes = hash(bytes, hashcount, bitsize);
for (long hash : hashes) {
// 只要有一个bit位为0,就确定不存在
if (!stringredistemplate.opsforvalue().getbit(key, hash)) {
return false;
}
}
return true;
}
// 多哈希函数实现(基于md5拆分)
private long[] hash(byte[] bytes, int hashcount, long bitsize) {
long[] hashes = new long[hashcount];
try {
messagedigest md5 = messagedigest.getinstance("md5");
byte[] digest = md5.digest(bytes);
// 把md5结果(16字节)拆分成多个哈希值
for (int i = 0; i < hashcount; i++) {
long hash = 0;
for (int j = i * 2; j < (i + 1) * 2 && j < digest.length; j++) {
hash = hash * 256 + (digest[j] & 0xff);
}
// 确保哈希值在bit数组长度范围内
hashes[i] = hash % bitsize;
}
} catch (nosuchalgorithmexception e) {
throw new runtimeexception("哈希函数初始化失败", e);
}
return hashes;
}
}// 初始化布隆过滤器:key为"user:bloom:filter",预估10万条数据,误判率0.01
redisbloomfilter bloomfilter = new redisbloomfilter("user:bloom:filter", 100000, 0.01, stringredistemplate);
// 添加元素
bloomfilter.add("user123");
bloomfilter.add("order456");
// 判断元素是否存在
boolean exists = bloomfilter.contains("user123"); // 大概率返回true
boolean notexists = bloomfilter.contains("user789"); // 一定返回false如果觉得手动实现麻烦,推荐用redisson——redis官方生态的java客户端,已经封装好了布隆过滤器,支持自动计算参数、分布式场景,还解决了手动实现的哈希函数优化问题,生产环境首选。
<dependency>
<groupid>org.redisson</groupid>
<artifactid>redisson-spring-boot-starter</artifactid>
<version>3.23.3</version> // 版本与redis版本适配
</dependency>import org.redisson.api.rbloomfilter;
import org.redisson.api.redissonclient;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;
@component
public class redissonbloomfilterdemo {
@autowired
private redissonclient redissonclient;
// 初始化布隆过滤器
public rbloomfilter<string> initbloomfilter() {
// 布隆过滤器名称
string filtername = "user:bloom:filter:redisson";
// 获取布隆过滤器实例
rbloomfilter<string> bloomfilter = redissonclient.getbloomfilter(filtername);
// 初始化:预估10万条数据,误判率0.01(redisson会自动计算m和k)
bloomfilter.tryinit(100000, 0.01);
return bloomfilter;
}
// 测试使用
public void testbloomfilter() {
rbloomfilter<string> bloomfilter = initbloomfilter();
// 添加元素
bloomfilter.add("user123");
bloomfilter.add("order456");
// 判断元素是否存在
boolean exists = bloomfilter.contains("user123"); // 大概率true
boolean notexists = bloomfilter.contains("user789"); // 一定false
// 统计已添加元素数量(近似值)
long count = bloomfilter.count();
system.out.println("已添加元素数量:" + count);
}
}redis布隆过滤器的核心价值的是“用极小空间换极高过滤效率”,落地时按场景选择实现方式:
其实布隆过滤器的逻辑并不复杂,核心就是“哈希标记+概率判断”。掌握它之后,面对缓存穿透、海量去重等问题,就不用再靠“全量存储”这种笨办法,能大幅提升系统性能和空间利用率。下次再遇到类似场景,直接掏出redis布隆过滤器,轻松搞定!
到此这篇关于redis快速实现布隆过滤器:缓存去重的“智能门卫”的文章就介绍到这了,更多相关redis布隆过滤器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论