2026/2/12 3:43:55
网站建设
项目流程
有可以做ssgsea的网站么,网站如何建设二级域名代理,国外建筑设计网站,怎么清空wordpress媒体库在缓存架构中#xff0c;总有一些“头疼问题”#xff1a;用户反复提交相同请求、查询不存在的key导致缓存穿透、海量数据去重效率低下……这些场景下#xff0c;Redis布隆过滤器就是当之无愧的“救星”。它像一个智能门卫#xff0c;能快速判断“这个人是不是来过”“这个…在缓存架构中总有一些“头疼问题”用户反复提交相同请求、查询不存在的key导致缓存穿透、海量数据去重效率低下……这些场景下Redis布隆过滤器就是当之无愧的“救星”。它像一个智能门卫能快速判断“这个人是不是来过”“这个key是不是不存在”用极小的空间成本实现高效过滤性能远超传统的数据库查询或全量缓存校验。今天咱们就从“是什么、为什么好用、怎么用Redis快速实现”三个维度用通俗的语言实操代码把布隆过滤器讲透。全程避开复杂公式就算是刚接触缓存的同学也能跟着步骤快速落地。一、先搞懂布隆过滤器到底是个啥布隆过滤器Bloom Filter本质是一个基于哈希函数的概率型数据结构核心作用是“快速判断一个元素是否存在于集合中”。它不像哈希表那样存储完整数据而是用一个二进制数组bit数组多个哈希函数通过标记元素的哈希位置来实现过滤。咱们用“小区门卫记访客”的场景类比秒懂核心逻辑二进制数组 门卫的登记本每一页只有“是”1和“否”0两个状态哈希函数 门卫的“记忆规则”比如“记住访客的姓氏首字母身高区间鞋子颜色”元素存在判断 门卫根据记忆规则核对登记本只要有一条规则对应“否”就确定访客没来过如果全是“是”则大概率来过存在极小误判。核心特性优点与“小瑕疵”布隆过滤器的优势和局限性都很鲜明落地前必须摸清✅ 核心优点空间占用极小仅用bit数组存储标记存储100万条数据误判率1%时仅需约1.2MB空间查询速度极快时间复杂度是O(k)k是哈希函数个数无论数据量多大都能瞬间返回结果支持海量数据无需存储完整数据可轻松应对千万级、亿级数据的过滤场景。❌ 不可忽视的局限性存在误判率只能确定“元素一定不存在”不能100%确定“元素一定存在”误判率可通过参数调整但无法完全消除不支持删除操作一旦元素被标记到bit数组无法反向清除会影响其他元素的判断需提前预估数据量哈希函数个数、bit数组长度需根据预估数据量计算否则会导致误判率飙升。二、Redis实现布隆过滤器的两种方式Redis本身没有内置布隆过滤器但提供了两种快速实现的方案一是基于Redis的BitMap位图手动实现灵活可控二是使用Redis官方推荐的Redisson客户端封装好现成API开箱即用。咱们分别讲实操按需选择即可。方案一基于BitMap手动实现灵活可控核心思路利用Redis的BitMap数据结构作为布隆过滤器的bit数组通过多个哈希函数计算元素的哈希值将对应位置的bit置为1查询时同样计算哈希值检查所有位置是否为1全为1则大概率存在否则一定不存在。1. 关键参数计算避免误判率过高手动实现前需先确定三个核心参数可通过公式或在线工具计算mbit数组长度单位bit预估数据量n越大m需越大k哈希函数个数k过多会导致bit数组快速被占满误判率上升k过少则过滤效果差p可接受的误判率通常设为0.01~0.1。常用计算公式无需死记在线工具直接算m - (n * ln p) / (ln 2)² bit数组长度k (m / n) * ln 2 哈希函数个数。举个例子预估存储10万条数据误判率设为0.01计算得m≈958505 bit约117KBk≈7个哈希函数。2. 手动实现代码Java示例核心是实现多个哈希函数操作Redis的BitMap指令SETBIT置1GETBIT查询/* by 01022.hk - online tools website : 01022.hk/zh/formatjs.html */ 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不存在返回falsetrue可能是误判 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; } }3. 使用方式/* by 01022.hk - online tools website : 01022.hk/zh/formatjs.html */ // 初始化布隆过滤器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客户端实现开箱即用如果觉得手动实现麻烦推荐用Redisson——Redis官方生态的Java客户端已经封装好了布隆过滤器支持自动计算参数、分布式场景还解决了手动实现的哈希函数优化问题生产环境首选。1. 引入依赖dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.23.3/version // 版本与Redis版本适配 /dependency2. 快速实现代码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 RBloomFilterString initBloomFilter() { // 布隆过滤器名称 String filterName user:bloom:filter:redisson; // 获取布隆过滤器实例 RBloomFilterString bloomFilter redissonClient.getBloomFilter(filterName); // 初始化预估10万条数据误判率0.01Redisson会自动计算m和k bloomFilter.tryInit(100000, 0.01); return bloomFilter; } // 测试使用 public void testBloomFilter() { RBloomFilterString 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); } }3. 核心优势分布式支持适配微服务场景多实例共享同一个布隆过滤器无需担心数据一致性参数优化内置更高效的哈希函数MurmurHash误判率控制更精准API丰富支持元素计数、批量添加等功能比手动实现更完善。三、实际应用场景与避坑指南✅ 典型应用场景缓存穿透防护查询数据库前先用布隆过滤器判断key是否存在不存在则直接返回避免大量无效数据库查询海量数据去重比如用户签到、日志去重、爬虫URL去重无需存储全量数据仅用bit数组标记防止重复提交接口请求前用布隆过滤器判断请求ID是否已处理避免重复业务逻辑执行黑名单过滤比如垃圾邮件识别、恶意IP拦截快速判断是否在黑名单中。❌ 避坑指南不要用在“绝对不能误判”的场景比如金融交易、用户登录验证误判可能导致严重问题提前预估数据量若实际数据量远超预估bit数组会被快速占满误判率会急剧上升可预留2~3倍冗余定期重置布隆过滤器若数据有过期特性比如每日黑名单更新可定期删除旧的布隆过滤器重建新实例Redis集群注意事项手动实现的布隆过滤器若用在Redis集群中需确保key落在同一个节点避免哈希分片导致bit数组分散Redisson已自动处理该问题。四、总结什么时候选哪种实现方式Redis布隆过滤器的核心价值的是“用极小空间换极高过滤效率”落地时按场景选择实现方式快速落地、生产环境、分布式场景选Redisson省心高效适配性强学习研究、自定义哈希函数、特殊参数需求选手动实现灵活可控加深对原理的理解。其实布隆过滤器的逻辑并不复杂核心就是“哈希标记概率判断”。掌握它之后面对缓存穿透、海量去重等问题就不用再靠“全量存储”这种笨办法能大幅提升系统性能和空间利用率。下次再遇到类似场景直接掏出Redis布隆过滤器轻松搞定❤️ 如果你喜欢这篇文章请点赞支持 同时欢迎关注我的博客获取更多精彩内容本文来自博客园作者佛祖让我来巡山转载请注明原文链接https://www.cnblogs.com/sun-10387834/p/19522155