缓存穿透-布隆过滤器
布隆过滤器
布隆过滤器由一个很长的bit数组和一系列哈希函数组成的概率型数据结构,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多。
布隆过滤器如何解决redis中的缓存穿透,首先也是对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。缺点是有一定的误识别率和删除困难。
一、问题产生背景
数据库中查询不到的数据一直穿库。
cache_key = "id:1";cache_value = GetValueFromRedis(cache_key); //判断缓存是否有数据if (cache_value != null) { //如果有 直接返回数据return cache_value;}db_value = GetValueFromDb(cache_key) // 从数据库中查询数据if (db_value == nulll) {return db_value;}expire_time = 300;SetRedisValue(cache_key, db_value, expire_time); //将数据库的结果更新到缓存中,并直接返回结果return db_value;
当数据库没有查询到对应的数据结果的时候,没有缓存到redis缓存中;对于这种无法命中的key就会一直穿库查询。
二、改进方法
【1】缓存无结果数据
cache_key = "id:1";cache_value = GetValueFromRedis(cache_key); //判断缓存是否有数据if (cache_value != null) { //如果有 直接返回数据return cache_value;}db_value = GetValueFromDb(cache_key) // 从数据库中查询数据if (db_value == null) {expire_time = 60; // 无数据,缓存一个null结果,缓存时间设置短一些SetRedisNullValue(cache_key, db_value, expire_time); } else {expire_time = 300; // 有数据,缓存时间设置长一些SetRedisValue(cache_key, db_value, expire_time);}return db_value;
这种方式会缓存数据库查询不到结果的key数据, redis产生无用数据,占用redis内存。
【2】布隆过滤器
cache_key = "id:1";cache_value = GetValueFromRedis(cache_key); //判断缓存是否有数据if (cache_value != null) { //如果有 直接返回数据return cache_value;}// 如果key在布隆过滤器中说明数据库中key没查出数据,直接返回
if (bloomFilter.contains(key)) {return null;}// 不在布隆过滤器中说明数据库中能查出数据或者是新key需要查库db_value = GetValueFromDb(cache_key) // 从数据库中查询数据if (db_value != null) {expire_time = 300; // 有数据,缓存时间设置长一些SetRedisValue(cache_key, db_value, expire_time);} else {// 数据库中没有查询出数据,将key添加到布隆过滤器中addToBloomFilter(cache_key);}return db_value;
布隆过滤器只能保证不在布隆过滤器集合中的key一定不存在,但是不能保证在布隆过滤器中的key一定存在因为存在hash冲突的情况,如果一个新的key hash过后值和老的key hash过后的值一样会认为该新key已经存在; 因此布隆过滤器是通过牺牲了一定的准确率来换取时间和空间的算法。
三、布隆过滤器应用的场景-去重
比如我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的?Redis 官方提供的布隆过滤器到了 Redis 4.0 提供了插件功能之后才正式登场。倘若数据量较小的情况下可以通过 查询数据库,过滤掉已经推送过的信息,剩下的就是没有被推送的了。当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录
问题是当用户量很大,每个用户看过的新闻又很多的情况下,这种方式,推荐系统的去重工作在性能上跟的上么?
如果历史记录存储在关系数据库里,去重就需要频繁地对数据库进行 exists 查询,当系统并发量很高时,数据库是很难扛住压力的。你可能又想到了缓存,但是如此多的历史记录全部缓存起来,那得浪费多大存储空间啊?而且这个存储空间是随着时间线性增长,你撑得住一个月,你能撑得住几年么?但是不缓存的话,性能又跟不上,这该怎么办?这时,布隆过滤器 (Bloom Filter) 闪亮登场了,它就是专门用来解决这种去重问题的。它在起到去重的同时,在空间上还能节省 90% 以上,只是稍微有那么点不精确,也就是有一定的误判概率。
四、注意事项
1、布隆过滤器的initial_size估计的过大,会浪费存储空间,估计的过小,就会影响准确率,用户在使用之前一定要尽可能地精确估计好元素数量,还需要加上一定的冗余空间以避免实际元素可能会意外高出估计值很多。
2、布隆过滤器的error_rate越小,需要的存储空间就越大,对于不需要过于精确的场合,error_rate设置稍大一点也无伤大雅。比如在新闻去重上而言,误判率高一点只会让小部分文章不能让合适的人看到,文章的整体阅读量不会因为这点误判率就带来巨大的改变。
3、使用时不要让实际元素远大于初始化大小,当实际元素开始超出初始化大小时,应该对布隆过滤器进行重建,重新分配一个 size 更大的过滤器,再将所有的历史元素批量 add 进去 (这就要求我们在其它的存储器中记录所有的历史元素)。因为 error_rate 不会因为数量超出就急剧增加,这就给我们重建过滤器提供了较为宽松的时间。
五、安装redis 布隆过滤器插件
1.下载redisbloom插件(redis官网下载即可)
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
2:解压并安装,生成.so文件
[root@redis]# tar -zxvf v1.1.1.tar.gz[root@redis]# cd Redisbloom-1.1.1/[root@redisbloom-1.1.1]# make[root@redisbloom-1.1.1]# lscontrib Dockerfile docs LICENSE Makefile mkdocs.yml ramp.yml README.md rebloom.so src tests
3:在redis配置文件(redis.conf)中加入该模块即可
[root@redis]# vim redis.conf#####################MODULES################# # Load modules at startup. If the server is not able to load modules
# it will abort. It is possible to use multiple loadmodule directives.
loadmodule /usr/local/redis/redisbloom-1.1.1/rebloom.so
4:重新启动redis
redis-server ./redis.conf
5:测试安装是否成功
127.0.0.1:6379> bf.add users user2 //写入数据user2
(integer) 1
127.0.0.1:6379> bf.add users user1 //写入数据user1
(integer) 1
127.0.0.1:6379> bf.exists users user1 //查询user1存在
(integer) 1
127.0.0.1:6379> bf.exists users user3 //查询user3不存在
(integer) 0
上面说过布隆过滤器存在误判的情况,在 redis 中有两个值决定布隆过滤器的准确率:
- error_rate :允许布隆过滤器的错误率,这个值越低过滤器的位数组的大小越大,占用空间也就越大。
- initial_size :布隆过滤器可以储存的元素个数,当实际存储的元素个数超过这个值之后,过滤器的准确率会下降。
redis 中有一个命令可以来设置这两个值:
bf.reserve users 0.01 100
三个参数的含义:
第一个值是过滤器的名字。
第二个值为 error_rate 的值。
第三个值为 initial_size 的值。
六、java应用中如何使用redis 布隆过滤器
1、基于redisson的java实现
RClusteredBloomFilter<SomeObject> bloomFilter = redisson.getClusteredBloomFilter("sample");// initialize Bloom filter with
// expectedInsertions = 255000000
// falseProbability = 0.03
bloomFilter.tryInit(255000000L, 0.03)bloomFilter.add(new SomeObject("field1Value", "field2Value"));bloomFilter.add(new SomeObject("field5Value", "field8Value"));bloomFilter.contains(new SomeObject("field1Value", "field8Value"));
2、基于lua脚本实现
【1】、往布隆过滤器添加key lua脚本
local values = KEYS
local bloomName = ARGV[1]
local result_1
for k,v in ipairs(values) doresult_1 = redis.call('BF.ADD',bloomName,v)
end
return result_1
【2】、判断key是否在布隆过滤器中 lua脚本
local values = KEYS
local bloomName = ARGV[1]
local result_1
for k,v in ipairs(values) doresult_1 = redis.call('BF.ADD',bloomName,v)
end
return result_1
java 中 jedis 如何使用lua脚本不再赘述
七、布隆过滤器算法推导过程
https://www.cnblogs.com/qdhxhz/p/11237246.html
缓存穿透-布隆过滤器相关推荐
- Redis缓存击穿和缓存雪崩、缓存穿透以及对应的解决方案
目录 缓存击穿 缓存击穿的解决方案 缓存雪崩 缓存雪崩的解决方案 缓存穿透 布隆过滤器 缓存击穿 一般我们会对缓存的key设置过期时间,在高并发下,如果在某一时刻这个key刚好过期,此时持续的大并发请 ...
- 布隆过滤器究竟是什么,这一篇给讲的明明白白的
作者:jack_xu juejin.im/post/5e9c110151882573793e8940 不知道从什么时候开始,本来默默无闻的布隆过滤器一下子名声大燥,在面试中面试官问到怎么避免缓存穿透, ...
- redis缓存穿透处理
第一种 简单的解决方法 针对这个情况,我们有一种简单的解决方法就是,在数据库没有查询该条数据的时候,我们让该key缓存一个 空数据,这样用户再次以该key请求后台的时候,会直接返回null,避免了再次 ...
- 解决Redis缓存穿透之布隆过滤器详解
文章目录 1. 什么是Bloom Filter(布隆过滤器) 1.1 布隆过滤器优点 1.2 布隆过滤器缺点 1.3 布隆过滤器使用场景 1.4 布隆过滤器检索过程 1.5 布隆过滤器的算法描述 2. ...
- java雪崩_【并发编程】java 如何解决redis缓存穿透、缓存雪崩(高性能示例代码)...
[并发编程]java 如何解决redis缓存穿透.缓存雪崩(高性能示例代码) 发布时间:2018-11-22 16:48, 浏览次数:872 , 标签: java redis <>缓存穿透 ...
- Redis缓存击穿,缓存穿透,缓存雪崩,附解决方案
前言 在日常的项目中,缓存的使用场景是比较多的.缓存是分布式系统中的重要组件,主要解决在高并发.大数据场景下,热点数据访问的性能问题,提高性能的数据快速访问.本文以Redis作为缓存时,针对常见的缓存 ...
- 什么是高并发高可用一致性?| 现代网站架构发展 | C 语言实现布隆过滤器
大话高并发高可用一致性|网站架构发展|网络编程缓存|C 语言实现布隆过滤器 Bloom Filter 编程练习 | GTest 教程 两个部分分为本文章,一部分是布隆过滤器的实现指引. 一个提供的前置 ...
- python redis 布隆过滤器实现
布隆过滤器是什么? 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路.但是随着 ...
- Redis 预防缓存穿透“神器” — 布隆过滤器
1. 布隆过滤器 1.1 概念 在架构设计时有一种最常见的设计被称为布隆过滤器,它可以有效减少缓存穿透的情况.其主旨是采用一个很长的二进制数组,通过一系列的 Hash 函数来确定该数据是否存在. 布隆 ...
最新文章
- 联想win10摁F2一直无法进入BIOS
- Apk文件破解反编译(转)
- python代码示例下载-python下xml解析库lxml最新版下载安装以及代码示例
- Post和Get差异
- matlab私有函数,MATLAB 嵌套函数,子函数,私有函数,重载函数
- 13/100. Best Time to Buy and Sell Stock
- java 创建定时器_SpringBoot创建定时任务
- 怎么用睡袋拉人_宝宝晚上翻身踢被子又着凉了,别再盖被子,给宝宝穿婴儿睡袋吧...
- python的科学计算库总结
- C++成员变量初始化列表中初始化顺序
- mariadb配置主从同步遇到的问题
- iOS动画和第三方插件学习网址
- ubunut16.04 更新python3.6
- 最新Hadoop的面试题总结
- 软件打不开且显示乱码的解决办法
- 3D打印机之Marlin固件配置
- JS统计页面访问时长
- 海龟交易法则(中译文)
- 利用路由器实现内网穿透
- 如何用VB实现半透明控件
热门文章
- iOS 第三方框架-SDWebImage
- php使用 memcache 来存储 session
- 模块和包——Python
- ERROR: Attempting to operate on hdfs namenode as root ERROR: but there is no HDFS_NAMENODE_USER defi
- 数据有什么特征和作用
- 美赛整理之带参数的常微分方程拟合问题研究
- linux汇编指令输出到屏幕,Linux 汇编语言(GNU GAS汇编)开发指南
- rds支持mysql自带函数吗_MySQL自定义函数(CREATE FUNCTION)
- 退火模拟算法c语言程序,C语言模拟退火算法(C language simulated annealing algorithm).doc...
- linux 终端 qmake,qt中的qmake命令设置