细谈布隆过滤器及Redis实现
何为布隆过滤器?
本质上是一种数据结构,是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。可以用于检索一个元素是否在一个集合中。
数据结构:
布隆过滤器是一个 bit 向量或者说 bit 数组,就是一个二进制的数据,数据存放0或1。如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,
并对每个生成的哈希值指向的 bit 位置为 1。
布隆过滤器的日常使用
在日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中。比如在字处理软件中,需要检查一个英语单词是否拼写正确(也就是要判断 它是否在已知的字典中);新闻客户端的推送去重功能,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录;在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上;在网络爬虫里,一个网址是否被访问过等等。
判断一个元素是否在集合中有很多方法,但是为啥会选择布隆过滤器呢?
将集合中全部的元素存在计算机中,遇到一个新 元素时,将它和集合中的元素直接比较即可。一般来讲,计算机中的集合是用哈希表(hash table)来存储的。它的好处是快速准确,缺点是费存储空间。当集合比较小时,这个问题不显著,但是当集合巨大时,哈希表存储效率低的问题就显现出来 了。比如说,一个象 Yahoo,Hotmail 和 Gmai 那样的公众电子邮件(email)提供商,总是需要过滤来自发送垃圾邮件的人(spamer)的垃圾邮件。一个办法就是记录下那些发垃圾邮件的 email 地址。由于那些发送者不停地在注册新的地址,全世界少说也有几十亿个发垃圾邮件的地址,将他们都存起来则需要大量的网络服务器。如果用哈希表,每存储一亿 个 email 地址, 就需要 1.6GB 的内存(用哈希表实现的具体办法是将每一个 email 地址对应成一个八字节的信息指纹, 然后将这些信息指纹存入哈希表,由于哈希表的存储效率一般只有 50%,因此一个 email 地址需要占用十六个字节。一亿个地址大约要 1.6GB, 即十六亿字节的内存)。因此存贮几十亿个邮件地址可能需要上百 GB 的内存。除非是超级计算机,一般服务器是无法存储的[1]。
其实这也就是需要说的布隆过滤器相对其他方式的优点:
高效地插入和查询,相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少。
当然缺点也很明显,就是有一定的误算概率。
布隆过滤器还有个特点,不支持删除,也不是完全不能,可以通过特殊手段实现,但是较为复杂,这边也不做细说。复杂的数学公式我们就不说了,我想也没有几个想了解的。
说点实用的,布隆过滤器如何在Redis中解决缓存击穿的。
处理redis的缓存穿透有两种方式:
一、缓存层缓存空值
将数据库中的空值也缓存到缓存层中,这样查询该空值就不会再访问DB,而是直接在缓存层访问就行。
但是这样有个弊端就是缓存太多空值占用了更多的空间,可以通过给缓存层空值设立一个较短的过期时间来解决。
二、布隆过滤器
将所有查询条件放入布隆过滤器中,数据请求时,现在布隆过滤器中查询,如果有,则继续查询操作,如果没有,则直接舍弃。
那么就来说说布隆过滤器是如何实现redis缓存穿透优化的,这边举两种常用方式:
1、使用google的guava工具实现:
pom添加依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>

demo代码
//初始化布隆过滤器
Long initLong = 1000000L;
BloomFilter<Long> bl = BloomFilter.create(Funnels.longFunnel(), 10000000, 0.01);
for(long l=0; l<initLong; l++){
bl.put(l);
}
try {
if(!bl.mightContain(userAccount)){
System.out.println("数据不存在");
return "error_code";//不存在的key为"stock"的直接返回
}
//下面是正常的redis操作,跟布隆过滤器没啥关系,可以忽略不看,测试key有所不同
Integer stock = Integer.valueOf(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("库存剩余:" + realStock);
} else {
System.out.println("库存不足!");
}
}finally {
if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
stringRedisTemplate.delete(lockKey);
}
}
主要使用到create和put已经mightContain三个方法,实现起来是不是很容易呢。
2、使用redisson为我们封装好的方法
同样的引入依赖包
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.15.2</version>
</dependency>
代码demo简单实现:
redis的简单配置
@Configuration
public class RedissonConfig { @Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Bean
public Redisson redisson(){
Config config = new Config();
config.useSingleServer().setAddress("redis://"+host+":"+port).setDatabase(0);
return (Redisson) Redisson.create(config);
}
}

布隆过滤器实现
@RequestMapping("/redisson/{userAccount}")
public String redisson(Long userAccount){
//模拟初始化布隆过滤器,可以不再局部内做
Long initLong = 1000000L;
BloomFilter<Long> bl = BloomFilter.create(Funnels.longFunnel(), 10000000, 0.01);
for(long l=0; l<initLong; l++){
bl.put(l);
}
if(!bl.mightContain(userAccount)){
System.out.println("数据不存在");
return "error_code";//不存在的key为"stock"的直接返回
}
String lockKey = "product_101";//分布式锁
RLock lock = redisson.getLock(lockKey);
try {
lock.lock();
//下面是正常的redis操作,跟布隆过滤器没啥关系,可以忽略不看,测试key有所不同
Integer stock = Integer.valueOf(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("库存剩余:" + realStock);
} else {
System.out.println("库存不足!");
}
}finally {
lock.unlock();
}
return "end";
}

总结:对于redis缓存穿透还有其他实现方式,常用的就是布隆过滤器,其原理其实就是这么简单,相信你们也都理解了,分享就这样吧。
参考文章:[1] https://www.cnblogs.com/liyulong1982/p/6013002.html
细谈布隆过滤器及Redis实现的更多相关文章
- 浅谈布隆过滤器Bloom Filter
先从一道面试题开始: 给A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL. 这个问题的本质在于判断一个元素是否在一个集合中.哈希表以O(1) ...
- OI视角浅谈布隆过滤器
简要谈及布隆过滤器 Preface 不负责的出题人扔了一道5e5,2M卡内存的题,标算布隆过滤器,然而std自己用std::set 70M碾过去了. 没学OI时候草草看过这个,不过忘得差不多了. 今天 ...
- 浅谈redis的HyperLogLog与布隆过滤器
首先,HyperLogLog与布隆过滤器都是针对大数据统计存储应用场景下的知名算法. HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现,布隆过滤器是在大数据情况下关于检索一个元 ...
- Redis: 缓存过期、缓存雪崩、缓存穿透、缓存击穿(热点)、缓存并发(热点)、多级缓存、布隆过滤器
Redis: 缓存过期.缓存雪崩.缓存穿透.缓存击穿(热点).缓存并发(热点).多级缓存.布隆过滤器 2019年08月18日 16:34:24 hanchao5272 阅读数 1026更多 分类专栏: ...
- 详细解析Redis中的布隆过滤器及其应用
欢迎关注微信公众号:万猫学社,每周一分享Java技术干货. 什么是布隆过滤器 布隆过滤器(Bloom Filter)是由Howard Bloom在1970年提出的一种比较巧妙的概率型数据结构,它可以告 ...
- Redis中的布隆过滤器及其应用
什么是布隆过滤器 布隆过滤器(Bloom Filter)是由Howard Bloom在1970年提出的一种比较巧妙的概率型数据结构,它可以告诉你某种东西一定不存在或者可能存在.当布隆过滤器说,某种东西 ...
- 布隆过滤器(Bloom Filter)简要介绍
一种节省空间的概率数据结构 布隆过滤器可以理解为一个不怎么精确的 set 结构,当你使用它的 contains 方法判断某个对象是否存在时,它可能会误判.但是布隆过滤器也不是特别不精确,只要参数设置的 ...
- Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?
使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...
- SpringBoot(18)---通过Lua脚本批量插入数据到Redis布隆过滤器
通过Lua脚本批量插入数据到布隆过滤器 有关布隆过滤器的原理之前写过一篇博客: 算法(3)---布隆过滤器原理 在实际开发过程中经常会做的一步操作,就是判断当前的key是否存在. 那这篇博客主要分为三 ...
随机推荐
- DC-2 靶机渗透测试
DC-2 靶机渗透测试 冲冲冲,好好学习. 本靶机核心内容"受限shell提权",知识点在另一篇文章中总结归纳了. 攻击机:kali 靶 机:DC-2 准备:在使用前需要在操作机的 ...
- 一张图带你搞懂Javascript原型链关系
在某天,我听了一个老师的公开课,一张图搞懂了原型链. 老师花两天时间理解.整理的,他讲了两个小时我们当时就听懂了. 今天我把他整理出来,分享给大家.也让我自己巩固加深一下. 就是这张图: 为了更好的图 ...
- Jetpack MVVM 实战项目,附带源码+视频,收藏!
从读者的反馈来看,近期大部分安卓开发已跳出舒适圈,开始尝试认识和应用 Jetpack MVVM 到实际的项目开发中. 只可惜,关于 Jetpack MVVM,网上多是 东拼西凑.人云亦云.通篇贴代码 ...
- C++实现链表的相关基础操作
链表的相关基础操作 # include <iostream> using namespace std; typedef struct LNode { int data; //结点的数据域 ...
- Vue响应式原理底层代码模拟实现
整体分析Vue的基本结构如下图所示:(备注:完整代码github地址https://github.com/1512955040/MiniVue) 上图中,为我们模拟最小vue的整体结构,首先创建一个v ...
- Python包安装过程
以下是paramiko-1.7.7.1的安装过程,可以看到整个过程分为步,第一步是build,就是拷贝源文件到build文件夹里, F:\VMFiles\tmpFiles\paramiko-1.7.7 ...
- AttributeError: module 'numpy' has no attribute 'num'
AttributeError: module 'numpy' has no attribute 'num' 写在前面 总的来说,先看看自己用的计算方式是不是写对了先,多个一起使用的话记得都看看 通过想 ...
- pikachu RCE远程系统命令执行
远程系统命令执行 一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口比如我们常见的路由器.防火墙.入侵检测等设备的web管理界面上一般会给用户提供一个ping操作的web界 ...
- CMD下编译运行Java程序
准备1:JDK下好了 准备2:环境变量配置好了 准备3:本次测试用的是JDK8,用JDK10的在Eclipse里面好像用不了 满足上面三个条件,那可以继续看下去了 Test_1:带包编译运行 代码: ...
- 05.SpringMVC之请求映射
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. @RequestMapping注解有六个属性,下面进 ...