前言

接上篇:用户浏览过数据排重 - A表查B表中不存在的数据(mysql),随着我们的数据量越来越大,需要排重过滤的数据越来越多,我们再去使用mysql来进行过滤排重,效率将会变得逐渐低下,最终我们需要一个新的解决方案:

数据库:数据量大、查询效率较低,必要时需要分库分表。

redis set(集合)排重:访问效率高,但是随着数据越来越大,需要的内存也会越来越多。

bloom filter(布隆过滤器):占用内存小,支持过期,但具有一定的误判率,100w数据,万分之一的误判率,才消耗7M左右的内存空间,。


最终我们可以考虑使用redis布隆过来器来实现,关于布隆过来器的介绍如下:

布隆过滤器(英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

原理:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。

优点:相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常数(O(k))。而且它不存储元素本身,在某些对保密要求非常严格的场合有优势。

缺点:一定的误识别率和删除困难。


安装redis bloom filter 扩展

在网络上有很多不同的语言版本(php、go、java)布隆过滤器实现,这里我们统一使用redis bloom filter 来演示,因为使用redis bloom filter更为方便,无需定义相关的储存逻辑和以及序列化、反序列化操作。

自从redis4.0起,redis便有了专门支持bloom filter的扩展了

# 拉下来redis的布隆过滤器扩展

git clone https://github.com/RedisBloom/RedisBloom.git
cd RedisBloom

# 编译后,会生成一个redisbloom.so文件

make

# 载入布隆过滤器扩展

redis-server --loadmodule ./redisbloom.so

redis bloom filter 操作命令

bf.add 添加元素到布隆过滤器

bf.exists 判断元素是否在布隆过滤器

bf.madd 添加多个元素到布隆过滤器,bf.add只能添加一个

bf.mexists 判断多个元素是否在布隆过滤器

bf.reserve 自定义布隆过滤器

bf.info 布隆过滤器的使用信息:初始化大小、误判率、当前使用数量(默认大小为:100,错误率2%)

127.0.0.1:6379> bf.add user:likes user:1:article:3

(integer) 1

127.0.0.1:6379> bf.add user:likes user:1:article:3

(integer) 1

127.0.0.1:6379> bf.add user:likes user:1:article:3

(integer) 1

127.0.0.1:6379> bf.exists user:likes user:1:article:3

(integer) 1

127.0.0.1:6379> bf.exists user:likes user:1:article:3

(integer) 1

127.0.0.1:6379> bf.exists user:likes user:1:article:3

(integer) 1

127.0.0.1:6379> bf.exists user:likes user:1:article:3

(integer) 0

127.0.0.1:6379> bf.madd user:likes user:1:article:3 user:1:article:4 user:1:article:5

(integer) 1

(integer) 1

(integer) 1

127.0.0.1:6379> bf.mexists user:likes user:1:article:3 user:1:article:4 user:1:article:5 user:1:article:5

1) (integer) 1

2) (integer) 1

3) (integer) 1

4) (integer) 0

127.0.0.1:6379> bf.info user:likes

Capacity 100

Size 290

Number of filters 1

Number of items inserted 1

Expansion rate 2

127.0.0.1:6379> bf.reserve test:like 0.0001 1000000

OK

通过布隆过滤器实战:用户是否浏览过某篇文章。

首先建立一个过滤器,用于储存所有的浏览数据,我们预计大概浏览量最终会达到10W,误判率为:0.0001,10W条数据中,将会有10条会被误判。

bf.reserve user:visits 0.0001 1000000

将用户浏览过的文章,通过一个字符串添加到过滤器中如: user:1:arctile:3,就是id为1的用户点赞的id为3的文章,如果有多条点赞内容,可以通过madd批量进行添加。

bf.add user:visits user:1:article:3

通过bf.exists命令判断用户是否浏览过该文章,浏览过的内容将会返回1,否则返回0。

bf.exists user:visits user:1:article:3