布隆过滤器 &布谷鸟过滤器 & Redis 安装布隆过滤器
1.布隆过滤器
1.1 简介
百度百科:布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
一句话就是 : 小空间解决大量数据匹配
1.2 数据匹配
如果我们自己来实现大量数据的匹配这样的算法实现会是什么样的?
答:用Map来存储数据
存在的问题就是,如果元素太多,会导致元素占用的空间比较大;
1.3 布隆过滤器实现
我们都是到哈希函数,哈希函数对于不论多长的数据,多大的数据经过哈希运算都可以得到固定长度的哈希值,而且大量数据得到的哈希值后分布也是相对均匀的;不同数据哈希运算后只会有极小概率其哈希值是一样的(哈希冲突或者哈希碰撞);
为了减少上面说的哈希冲突或者哈希碰撞,布隆过滤器是采用多次哈希的方式;
如下图:
元素123 经过三个哈希算法 HASH1,HASH2,HASH3 把计算的数据映射到bitmap 中去
- 允许多个元素重复映射 比如元素1 HASH3 和 元素2 HASH3 是一样的,元素2 HASH2 和 元素3 HASH1 是一样的
- 布隆过滤器判断元素存在,元素可能不存在,也可能存在,允许失败判断
- 布隆过滤器判断元素不存在,元素一定不存在
通过上面的图也可以直接看到布隆过滤器的缺点:
-
不能删除数据,如果删除数据,极大可能会影响到其他的数据,从而影响到数据的判断
-
多个哈希函数计算出来的哈希值分布较大 ,多次读取内存,CPU寻址花销较大
1.4 内存占用
- n: 总的数据量的数量大小。
- p: 误判率的大小 (0.01 means 1%)
- k: hash函数的数量
- m: 需要的Bit数组的空间量。
m = ceil((n*log§)/log(1/pow(2,log(2))))
示例 k取5时的误判率小于5%,此时的系数约等于7;1000000数据需要 7000000 bit ==> 875KB
2.Redis 服务端和客户端实现布隆过滤器
当我们借助Redis 来实现布隆过滤器的时候,从客户端和服务端两种角度来康,会有不同二种不同的实现;
2.1 Redis 实现算法和存储
轻量级客户端,客户端不做如何实现,服务端实现算法和bitmap的数据结构,下面的安装是这个模式
2.1 Redis 实现算法
客户端实现算法,服务端实现bitmap的数据结构;
3.Redis 安装布隆过滤器
3.1 安装准备
首先是系统上 Redis 已经安装,然后下载 RedisBloom 的源码,不推荐使用master 分支代码,编译的时候会报错,我这里使用的是最新的tag
https://github.com/RedisBloom/RedisBloom.git
3.2 编译安装
下载后解压进入目录后执行编译
make
编译后得到扩展库
redisbloom.so
复制扩展库到bin目录的同级目录
[root@localhost RedisBloom-2.2.14]# cp redisbloom.so /soft/installdir/redis6/
[root@localhost RedisBloom-2.2.14]# cd /soft/installdir/redis6/
[root@localhost redis6]# ls
bin redisbloom.so
3.3 启动
停止redis 服务
service redis_6379 stop
启动并加载组件
[root@localhost redis6]# redis-server /etc/redis/6379.conf --loadmodule /soft/installdir/redis6/redisbloom.so
3.4 使用
上面3.1 中说了是Redis服务端实现了算法和bitmap 存储,所以说客户单是直接使用就好了
127.0.0.1:6379> BF.ADD sff aa
(integer) 1
127.0.0.1:6379> BF.ADD sff bb
(integer) 1
127.0.0.1:6379> BF.ADD sff cc
(integer) 1
127.0.0.1:6379> BF.ADD sff dd
(integer) 1
127.0.0.1:6379> BF.EXISTS sff ee
(integer) 0
127.0.0.1:6379> BF.EXISTS sff aa
(integer) 1
127.0.0.1:637
4.升级版本
对于布隆过滤器的缺点出现了一些其他的过滤器来弥补布隆过滤器的缺点;比如
4.1 增强版的布隆过滤器(Counting Bloom Filter)
bitmap 换成数组,数组里面存储的数据的次数,删除的时候-1,添加数据的时候+1
4.2布谷鸟过滤器
type bucket [4]byte // 一个桶,4个座位
//过滤器数据结构
type cuckoo_filter struct {
buckets [size]bucket // 一维数组 每个数组都是一个桶
nums int // 容纳的元素个数
kick_max // 最大挤兑次数
}
插入:首先计算数据的指纹和哈希值,并通过指纹和哈希值计算另一个哈希值,两个哈希值映射到两个位置(因为计算得到两个位置,每个位置存储4个指纹,所以相同对象最多存储8个,可以这样理解 相当于HashMap ,数据计算会得到两个 index ,每个index上面挂载4 个 node ,理论最打可以插入相同数据8 个)。接下来进行插入,会尝试插入两个位置,如果都失败,随机挤走一个指纹,并重新为该指纹寻找新的位置(超过最大挤兑次数后扩容) 如下图。
布谷鸟过滤器巧妙的地方就在于设计了一个独特的 hash 函数,使得可以根据 p1 和 元素指纹 直接计算出 p2,而不需要完整的 x 元素;
fp = fingerprint(x) //计算指纹
p1 = hash(x) //位置1 HASH1
p2 = p1 ^ hash(fp) //位置2 HASH2 不需要原始数据就可以计算,挤兑的时候直接使用指纹和原来的位置计算挤兑后的位置
扩容:如果数组过小,会发生循环挤兑的情况,就可以设置最大挤兑次数,如果超过该次数,进行扩容,重新计算每个指纹的位置。
查找:通过计算哈希值得到两个元素,对两个元素中的8个位置进行指纹对比(上图对比数据),如果对比成功则表示数据存在 只要其中一个相同就成功。如果哈希值和指纹相同时会发生误判(小概率,一个指纹1byte 8 bit表示 256 个方式 )。
删除:因为每个对象的指纹会存储到一个位置中,所以可以通过删除这个指纹来删除数据。删除功能无法使用的情况:如果相同对象存储超过8个,就无法使用删除功能;如果俩数据的哈希值和指纹相同时,会出现误删除情况。
更新:即删除后再添加新指纹