何为布隆过滤器?

本质上是一种数据结构,是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实现的更多相关文章

  1. 浅谈布隆过滤器Bloom Filter

    先从一道面试题开始: 给A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL. 这个问题的本质在于判断一个元素是否在一个集合中.哈希表以O(1) ...

  2. OI视角浅谈布隆过滤器

    简要谈及布隆过滤器 Preface 不负责的出题人扔了一道5e5,2M卡内存的题,标算布隆过滤器,然而std自己用std::set 70M碾过去了. 没学OI时候草草看过这个,不过忘得差不多了. 今天 ...

  3. 浅谈redis的HyperLogLog与布隆过滤器

    首先,HyperLogLog与布隆过滤器都是针对大数据统计存储应用场景下的知名算法. HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现,布隆过滤器是在大数据情况下关于检索一个元 ...

  4. Redis: 缓存过期、缓存雪崩、缓存穿透、缓存击穿(热点)、缓存并发(热点)、多级缓存、布隆过滤器

    Redis: 缓存过期.缓存雪崩.缓存穿透.缓存击穿(热点).缓存并发(热点).多级缓存.布隆过滤器 2019年08月18日 16:34:24 hanchao5272 阅读数 1026更多 分类专栏: ...

  5. 详细解析Redis中的布隆过滤器及其应用

    欢迎关注微信公众号:万猫学社,每周一分享Java技术干货. 什么是布隆过滤器 布隆过滤器(Bloom Filter)是由Howard Bloom在1970年提出的一种比较巧妙的概率型数据结构,它可以告 ...

  6. Redis中的布隆过滤器及其应用

    什么是布隆过滤器 布隆过滤器(Bloom Filter)是由Howard Bloom在1970年提出的一种比较巧妙的概率型数据结构,它可以告诉你某种东西一定不存在或者可能存在.当布隆过滤器说,某种东西 ...

  7. 布隆过滤器(Bloom Filter)简要介绍

    一种节省空间的概率数据结构 布隆过滤器可以理解为一个不怎么精确的 set 结构,当你使用它的 contains 方法判断某个对象是否存在时,它可能会误判.但是布隆过滤器也不是特别不精确,只要参数设置的 ...

  8. Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?

    使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...

  9. SpringBoot(18)---通过Lua脚本批量插入数据到Redis布隆过滤器

    通过Lua脚本批量插入数据到布隆过滤器 有关布隆过滤器的原理之前写过一篇博客: 算法(3)---布隆过滤器原理 在实际开发过程中经常会做的一步操作,就是判断当前的key是否存在. 那这篇博客主要分为三 ...

随机推荐

  1. WPF DataGrid DataGridTextColumn

    Style设置时,无法绑定到数据,需要这样写 1 <DataGridTextColumn Header="呵呵" Binding="{Binding ID}&quo ...

  2. Quartz部署Linux的一个坑

    前言 最近做了一个项目,使用Quartz做定时任务,然后部署到了Linux服务器上,但是竟然很惊奇的跑不起来,已经在阿里云上的Linux上验证无数次了,后来经过不懈努力,终于发现了问题,我自己的Lin ...

  3. 探讨UE4中的UBT和UHT

    前言 UBT和UHT是编译工具,谁定义的呢,虚幻引擎自己定义的,拿来做什么呢,UBT和UHT是UE4用来简化多平台编译,去除用户自定义平台编译项目的操作 我们写的UE4代码不是标准的C++代码,是基于 ...

  4. SpringBoot - 集成Auth0 JWT

    目录 前言 session认证与Token认证 session认证 Token认证 JWT简介 JWT定义 JWT数据结构 JWT的类库 具体实现 JWT配置 JWT工具类 测试接口 前言 说说JWT ...

  5. 从一次netty 内存泄露问题来看netty对POST请求的解析

    背景 最近生产环境一个基于 netty 的网关服务频繁 full gc 观察内存占用,并把时间维度拉的比较长,可以看到可用内存有明显的下降趋势 出现这种情况,按往常的经验,多半是内存泄露了 问题定位 ...

  6. vue--三种组件中之间的传值

    参考网址:https://www.jianshu.com/p/46573a741c29 一.父子组件之间的传值----props/$emit 组件之间的传值,我们比较常用到的是props/$emit ...

  7. SQL查询对分数进行排名

    编写SQL查询以对分数进行排名. 如果两个分数之间存在平局,则两者应具有相同的排名. 请注意,在平局之后,下一个排名数应该是下一个连续的整数值. 换句话说,等级之间不应该存在"漏洞" ...

  8. 【springcloud】服务熔断与降级(Hystrix)

    转自:https://blog.csdn.net/pengjunlee/article/details/86688858 服务熔断 服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的 ...

  9. WPF 勾选划线

    最近项目需要一个左右侧一对多的划线功能 我们先来看一下效果秃: 主要功能: 支持动态添加 支持复选 支持修改颜色 支持动态宽度 主要实现:事件的传递 应用场景:购物互选,食品搭配,角色互选 数据源 左 ...

  10. css - 响应式

    css - 响应式 移动设备尺寸 移动设备的尺寸各不相同,大体上可以做如下划分: 768px以下的是手机屏幕 768px-991px是平板ipad屏幕 992px-1199是大平板屏幕 1200极其以 ...