在Redis缓存击穿(失效)、缓存穿透、缓存雪崩如何解决?我们提到可以使用布隆过滤器来避免“缓存穿透”。麻麻,Bloomfilter可以在什么场景下使用呢?比如我们用“马哥跳”开发的“明天头条”APP看新闻。如何保证每次推荐给用户的内容都不会重复,并且过滤了已经看过的内容呢?你会说我们只需要记录每个用户浏览过的历史记录,每次推荐时查询数据库过滤已有的数据就可以实现去重。事实上,如果历史记录存储在关系数据库中,去重需要对数据库进行频繁的exists查询。当系统并发量很高时,数据库难以承受压力。代码兄,我可以使用缓存将历史数据存储在Redis中。绝对不会,这么多历史记录要浪费多少内存空间,所以这个时候我们可以使用布隆过滤器来解决这个去重问题。速度快又省内存,互联网开发必备!当遇到大量数据需要去重时,可以考虑布隆过滤器。以下场景:解决Redis缓存穿透问题(面试重点)。邮件过滤,使用布隆过滤器实现邮件黑名单过滤。对爬虫爬取的网站进行过滤,爬取到的网站不再爬取。推荐的新闻不再推荐。什么是布隆过滤器布隆过滤器(BloomFilter)由BurtonHowardBloom于1970年提出,是一种空间高效的概率数据结构,用于判断一个元素是否在一个集合中。当布隆过滤器说某个数据存在时,这个数据可能不存在;当布隆过滤器说某个数据不存在时,那么这个数据一定不存在。哈希表也可以用来判断一个元素是否在一个集合中,但是布隆过滤器只需要哈希表的1/8或1/4的空间复杂度就可以完成同样的问题。布隆过滤器可以插入元素,但不能删除已有元素。元素越多,假阳性率(falsepositiverate)越大,但是假阴性(missingnegative)是不可能的。布隆过滤器原理BloomFilter的算法是先分配一块内存空间作为位数组,数组的位的初始值全部设置为0。在添加元素时,使用k个独立的Hash函数来计算,然后将元素Hashmap的K个位置全部置1。检测key是否存在,还是用这k个Hash函数计算k个位置。如果所有位置都为1,则说明key存在,否则不存在。如下图所示:Bloomfilter原理哈希函数会发生碰撞,所以Bloomfilter会产生误判。这里的误报率指的是BloomFilter判断一个key存在,但实际上并不存在的概率,因为它存储的是key的Hash值,而不是key的值。所以有概率存在这样的key,虽然内容不一样,但是多次Hash后的Hash值是一样的。对于BloomFilter判断不存在的key,100%不存在。如果key存在,那么它在每个Hash之后对应的Hash值位置一定是1,而不是0。Bloomfilter不一定存在。代码大哥,为什么不允许删除元素?删除是指对应的k位需要置0,可能是其他元素对应的位。所以remove会引入漏报,这是绝对不允许的。当Redis集成BloomfilterRedis4.0正式提供插件机制,Bloomfilter正式登场。以下网址可以下载官方编译好的可扩展模块。https://redis.com/redis-enterprise-software/download-center/modules/。RedisModules码兄推荐使用Redis6.x版本,最低4.x版本集成Bloomfilter。使用以下命令检查版本。Mage安装的版本是6.2.6。redis-server-vRedisserverv=6.2.6sha=00000000:0malloc=libcbits=64build=b5524b65e12bbef5下载我们自己编译安装,需要到github上下载,目前release版本是v2.2.14,下载地址:https://github.com/RedisBloom/RedisBloom/releases/tag/v2.2.14。RedisBloom解压编译解压:tar-zxvfRedisBloom-2.2.14.tar编译插件:cdRedisBloom-2.2.14make编译成功,会看到redisbloom.so文件。安装集成需要修改redis.conf文件,添加loadmodule配置,重启Redis。loadmodule/opt/app/RedisBloom-2.2.14/redisbloom.so如果是集群,需要在配置中加入各个实例的配置文件。模块配置指定配置文件并启动Redis:redis-server/opt/app/redis-6.2.6/redis.conf加载成功的页面如下:Bloomfilter加载成功,客户端连接Redis进行测试。BF.ADD--向布隆过滤器添加一个元素BF.EXISTS--判断元素是否在布隆过滤器中BF.MADD--向布隆过滤器中添加多个元素BF.MEXISTS--判断多个元素是否在布隆过滤器中Redis布隆过滤器中的布隆过滤器。下面我们使用布隆过滤器来解决缓存穿透问题。缓存穿透:是指有一个特殊的请求去查询一个不存在的数据,即该数据不存在于Redis中,也不存在于数据库中。当用户购买产品并创建订单时,我们向mq发送消息并将订单ID添加到布隆过滤器。订单同步到布隆过滤器在添加到布隆过滤器之前,我们手动创建一个BF.RESERVE命令,名称为orderserror_rate=0.1,初始容量为10000000布隆过滤器:#BF.RESERVE{key}{error_rate}{capacity}[EXPANSION{expansion}][NONSCALING]BF.RESERVE命令0.110000000key:过滤器名称。error_rate:预期错误率,默认0.1,值越低,需要的空间越大。capacity:初始容量,默认为100,当实际元素个数超过这个初始容量时,误判率会增加。EXPANSION:可选参数,当加入Bloomfilter的数据达到初始容量时,Bloomfilter会自动创建子filter,子filter的大小为上一个filter的大小乘以expansion;expansion为默认值为2,表示Bloomfilter的扩容默认是扩容的2倍。NONSCALING:可选参数。设置后,当加入布隆过滤器的数据达到初始容量时,过滤器将不会扩容,并抛出异常((错误)ERRnonscalingfilterisfull)。说明:BloomFilter的扩容是通过增加BloomFilter的层数来完成的。每增加一层,就可以遍历多层BloomFilter来完成查询,每层的容量都是上一层的两倍(默认)。如果不使用BF.RESERVE命令创建,而是使用Redis自动创建的Bloomfilter,默认error_rate为0.01,capacity为100。Bloomfilter的error_rate越小,需要的存储空间越大.对于不需要太精确的场景,error_rate设置可以稍微大一点。布隆过滤器的容量设置太大会浪费存储空间,设置太小又会影响准确率。因此,在使用之前,必须尽可能准确地估计出元素的数量,并且需要加入一定的冗余度。空间以避免实际元素可能意外地比设置值高很多。将订单ID添加到过滤器#BF.ADD{key}{item}BF.ADDorders10086(integer)1使用BF.ADD将元素10086添加到名为orders的布隆过滤器中。如果同时添加多个元素,使用BF.MADDkey{item...},如下:BF.MADDorders10087100891)(integer)12)(integer)1判断订单是否存在#BF.EXISTS{key}{item}BF.EXISTSorders10086(integer)1BF.EXISTS判断一个元素是否存在于BloomFilter中,返回值=1表示存在。如果需要批量检查布隆过滤器是否存在多个元素,使用BF.MEXISTS,返回值为数组:1:存在。0:不存在。#BF.MEXISTS{key}{item}BF.MEXISTSorders100100891)(integer)02)(integer)1一般来说,我们可以通过三个指令来避免缓存:BF.RESERVE、BF.ADD、BF.EXISTS穿透问题。码兄,如何查看创建的Bloomfilter信息?用BF.INFOkey查看,如下:BF.INFOorders1)Capacity2)(integer)100000003)Size4)(integer)77941845)Numberoffilters6)(integer)17)Numberofitemsinserted8)(integer)39)Expansionrate10)(integer)2返回值:Capacity:预设容量。尺寸:实际入住,但如何计算以进一步确认为准。Numberoffilters:过滤层数。Numberofitemsinserted:实际插入的元素个数。Expansionrate:子过滤器扩展因子(默认2)。码哥,布隆过滤器怎么删除?目前Bloomfilter不支持删除,但CuckooFilter支持删除。Bloom过滤器通常在插入项目时表现出更好的性能和可扩展性(因此,如果您经常向数据集添加项目,Bloom过滤器可能是理想的选择)。Cuckoo过滤器在检查操作上速度更快,并且还允许删除。有兴趣的可以看看:https://oss.redis.com/redisbloom/Cuckoo_Commands/)。马哥,我想知道你是怎么掌握这么多技术的?其实我只是看了官方文档,做了一些简单的处理。本文内容基于Redis官方文档中的上述示例:https://oss.redis.com/redisbloom/。遇到问题要耐心地从官方文档中寻找答案,培养自己阅读问题和定位问题的能力。RedissionBloomfilter实战码哥示例代码基于SpringBoot2.1.4,代码地址:https://github.com/MageByte-Zero/springboot-parent-pom。添加Redission依赖:
