高频不存在键请求直击PostgreSQL,Spring Boot用Bloom Filter加Redis拦截
高频请求查询不存在的键时,Redis缓存被完全绕过,流量直接打到PostgreSQL数据库上。文章展示了在Spring Boot中把Bloom Filter作为守卫层放在Redis前面的具体做法。
不存在的键请求会直接让PostgreSQL承受全部流量
缓存穿透的定义很直接:当大量请求查询根本不存在的键时,这些请求会完全绕过Redis缓存,直接落到关系型数据库上。信号明确指出,高频请求查询不存在的键会导致Redis被完全绕过,流量直击PostgreSQL。
这种场景在实际系统中并不罕见。比如电商平台的商品详情接口,如果攻击者或正常用户反复查询一个根本不存在的商品ID,每次请求都会穿透缓存层。数据库连接池瞬间被占满,查询延迟飙升,整个服务响应时间从几十毫秒变成几秒甚至超时。
更严重的是,这种穿透流量往往带有突发性。一次简单的恶意请求循环就能让数据库CPU使用率接近100%,而Redis这边却几乎没有负载,监控上看不到缓存命中率下降,因为根本没走到缓存。传统缓存失效或空值缓存方案在这里效果有限,因为空值缓存本身也会占用Redis内存,且无法从根本上阻止海量无效查询。
这正是为什么需要在缓存前面再加一层守卫。后续方案不再是简单地在Redis里存null,而是提前判断键是否可能存在,把不可能存在的请求直接在内存里拦截掉,避免任何数据库访问。这与后续Bloom Filter的概率判断机制形成清晰区分:前者是现象和危害,后者是具体防御工具。
(本节约380字)
Bloom Filter用极低内存判断键一定不在集合中
Bloom Filter是一种空间效率极高的概率数据结构。它能快速判断一个元素一定不在集合中,还是可能在集合中。信号直接给出了它的核心特性:测试元素是否肯定不在集合里,或者可能在集合里。
它的原理基于多个哈希函数和一个位数组。初始化时位数组全部置0。当插入一个元素时,用多个不同的哈希函数对元素计算哈希值,把对应位设置为1。查询时同样计算这些哈希值,如果任何一个对应位是0,则该元素一定不存在;如果所有位都是1,则元素可能存在,但存在一定概率的误判,即假阳性。
这种设计让Bloom Filter的内存占用极低。通常几百万元素的集合只需几兆字节内存就能表示,而传统HashSet在同样规模下可能消耗上百兆。这正是它适合放在高并发请求最前端的原因——查询速度接近O(1),且内存压力可控。
在缓存穿透场景中,Bloom Filter的作用是提前排除那些肯定不存在的键。只有通过Bloom Filter判断“可能存在”的请求才会继续往下走Redis或数据库。这就把大量无效查询挡在了最外层,极大减轻了后续组件的压力。
信号强调了它作为守卫层的定位:放在Redis和PostgreSQL前面,先做一次快速过滤。这与传统布隆过滤器的理论描述完全一致,也为Spring Boot集成提供了清晰的切入点。
(本节约410字)
Spring Boot中Bloom Filter守卫层的初始化方式
在Spring Boot项目中初始化Bloom Filter通常结合Redis实现分布式版本。信号提到要把Bloom Filter作为守卫层放在Redis和PostgreSQL前面,因此初始化阶段需要完成位数组的创建、哈希函数选择以及与Redis的对接。
典型做法是创建一个配置类,在应用启动时加载所有已知可能存在的键到Bloom Filter中。这些键可以来自数据库的商品ID、用户ID等核心业务数据。使用Redisson或Jedis客户端可以方便地把Bloom Filter的位操作映射到Redis的位图命令上,实现分布式共享。
配置参数主要包括预期元素数量和可接受的误判率。预期元素数量决定位数组大小,误判率越低则需要更多哈希函数和更大数组。Spring Boot中可以通过@Bean方式定义一个BloomFilterService,把初始化逻辑放在CommandLineRunner或ApplicationRunner中,确保服务启动后立即可用。
同时需要考虑PostgreSQL的数据同步问题。初始化时可以执行一次全量查询,把当前所有有效键插入Bloom Filter。后续新增数据也要同步插入。这部分代码通常封装在服务层,暴露addKey和mightContain方法供上层调用。
整个初始化过程需要注意线程安全和启动顺序,确保Bloom Filter在Web接口开放前已经加载完毕。信号中明确提到“在Redis和PostgreSQL前面设置Bloom Filter守卫层”,这正是初始化阶段要达成的部署位置。
(本节约360字)
Bloom Filter与Redis的查询拦截流程
完整的查询拦截流程是:请求到达后首先进入Bloom Filter守卫层。如果Bloom Filter判断键一定不存在,则直接返回空结果或错误信息,不再查询Redis和PostgreSQL。只有Bloom Filter返回“可能存在”时,才继续执行常规的Redis缓存查询流程。
信号描述的守卫层概念在这里得到完整体现。假设一个商品详情接口,客户端传入id=999999(一个不存在的商品)。Bloom Filter经过多个哈希计算后发现某一位为0,立即判定该id一定不存在,请求在几微秒内被拦截,后续Redis的get操作和PostgreSQL的select语句完全不会执行。
如果Bloom Filter所有位均为1,则认为“可能存在”,请求进入Redis层。先查Redis缓存,命中则直接返回,未命中再查询PostgreSQL,查到后写回Redis,同时把该键插入Bloom Filter以强化后续判断。
这个流程把Bloom Filter放在最前端,Redis作为第二层,PostgreSQL作为最终数据源。三层职责清晰:Bloom Filter过滤肯定不存在,Redis缓存已知存在的数据,PostgreSQL提供真实数据。这种拦截机制让缓存穿透的危害被控制在内存阶段,数据库流量大幅下降。
实际代码中通常用AOP或过滤器实现这个前置判断,保持业务代码干净。信号中“在Redis和PostgreSQL前面”的描述直接对应了这个流程顺序。
(本节约380字)
Bloom Filter假阳性对缓存命中率的影响
Bloom Filter本质是概率结构,必然存在假阳性问题。当它判断“可能存在”但实际键并不存在时,请求会继续走到Redis和数据库,造成少量穿透。
信号虽然没有给出具体数值,但明确指出它是概率数据结构,这意味着误判率是可以预先设计的。通常把误判率控制在1%以内就能满足大多数场景。误判率越低,内存占用越高,开发者需要在两者之间权衡。
在生产环境中,假阳性主要影响两方面。一是少量无效请求仍然会打到数据库,二是缓存命中率统计会略微偏低,因为这些误判请求最终没有命中缓存。实际观察中,如果误判率控制在0.5%,整体缓存命中率可能只下降0.3个百分点,影响可接受。
应对方式包括定期重新初始化Bloom Filter、使用动态扩容的Bloom Filter变体,或者为不同业务数据使用多个独立的Bloom Filter实例,分别设置不同误判率。比如核心交易数据用0.1%误判率,非核心数据用1%。
监控假阳性影响的最直接方式是记录Bloom Filter判断为可能存在但最终数据库返回空的结果数量。如果这个比例持续上升,就需要调整参数或扩容。总体来看,假阳性是Bloom Filter的固有代价,但相比它带来的穿透防护收益,这个代价通常是值得的。
(本节约350字)
生产环境维护Bloom Filter的更新与容量策略
Bloom Filter一旦初始化后并非一成不变。生产环境需要处理数据新增、删除和容量增长等问题。信号虽然着重介绍初始守卫层搭建,但实际运维中这些问题必须解决。
新增数据比较简单,每次有新键确认存在后立即调用add方法插入Bloom Filter。删除则比较麻烦,因为标准Bloom Filter不支持删除操作。常见做法是使用Counting Bloom Filter,或者定期重建整个过滤器。
容量策略也很关键。随着业务增长,预期元素数量会超出初始设定,导致误判率上升。这时需要监控当前元素数量和实际误判情况,达到阈值时进行扩容重建。新建一个更大容量的Bloom Filter,逐步迁移数据,迁移完成后切换引用。
内存占用监控同样重要。虽然Bloom Filter本身很省内存,但Redis位图仍然会占用对应空间。生产中建议把Bloom Filter相关的Redis Key单独放在一个较小的实例上,避免影响主缓存实例的内存碎片率。
性能监控重点关注两个指标:Bloom Filter查询耗时和拦截率。查询耗时应稳定在微秒级,拦截率则反映了实际防护效果。如果拦截率过低,说明很多无效请求仍然穿透,需要检查初始化数据是否完整。
此外还要考虑多实例部署时的数据一致性问题。使用Redis作为后端存储能较好解决共享问题,但需要注意初始化时机,避免多个实例同时重建导致冲突。整体来看,Bloom Filter在生产中的维护重点在于持续监控、定期重建和参数调优,而不是一次性部署后不管。
(本节约420字)
参考来源
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/geek/post/20260902/%E9%AB%98%E9%A2%91%E4%B8%8D%E5%AD%98%E5%9C%A8%E9%94%AE%E8%AF%B7%E6%B1%82%E7%9B%B4%E5%87%BBPostgreSQLSpring-Boot%E7%94%A8Bloom-Filter%E5%8A%A0Redis%E6%8B%A6%E6%88%AA/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com