这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战
布隆过滤器的思想
如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。
Hash表的数据结构
不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。
布隆过滤器的Hash算法
Hash面临的问题就是冲突。假设Hash函数是良好的,如果我们的位阵列长度为m个点,那么如果我们想将冲突率降低到例如 1%, 这个散列表就只能容纳m / 100个元素。显然这就不叫空间效率了(Space-efficient)了。解决方法也简单,就是使用多个Hash,如果它们有一个说元素不在集合中,那肯定就不在。如果它们都说在,虽然也有一定可能性它们在说谎,不过直觉上判断这种事情的概率是比较低的。
纯Java实现的方案
1 | java复制代码 public class MyBloomFilter { |
Redis实现布隆过滤器
布隆过滤器介绍
布隆过滤器是一个很长的二进制向量和一系列随机映射函数,适用于判断某个数据在集合中是否存在,会存在误识别。
- 优点是空间效率和查询时间都比一般的算法要好的多
- 缺点是有一定的误识别率和删除困难。
布隆过滤器使用场景
客户端–布隆过滤器(hashmap)-redis缓存–DB数据库
- 在程序启动时,将redis的所有key先缓存预热(加载)到布隆过滤器中,也可以用hashmap,但布隆过滤器的性能会比hashmap快很多。
- 客户端请求的时候,先经过布隆过滤器,判断key是否存在,不存在的话,直接返回,可以解决redis的穿透和击穿。
- 布隆过滤器误判经过redis里,也不会造成原先大批量的涌入,这是可以接受的
- 如果redis的key有所变更,让布隆过滤器重新缓存预热,可解决删除问题
布隆过滤器存在的问题
误判
- Jarye2本身在二进制向量表中不存在,由于hash值和其他碰撞,导致以为存在。
- 解决方式: 把误判概率设置的足够小,但会导致向量表会大很多。
删除困难
加入把Jarye2删了,会把向量地址8 13的值设置为0,导致原先应该命中的Jarye1无法命中
**解决方式:**缓存重新预热。
布隆过滤器demo示例
1 | xml复制代码<dependency> |
1 | csharp复制代码/** |
基于布隆过滤器解决Redis击穿问题
1 | kotlin复制代码@RequestMapping("/getOrder") |
资料参考
本文转载自: 掘金