过滤器系列(二)—— Cuckoo filter
这一篇讲的是布谷过滤器(cuckoo fliter),这个名字来源于更早发表的布谷散列(cuckoo hash),尽管我也不知道为什么当初要给这种散列表起个鸟名=_=
由于布谷过滤器本身的思想就源自于布谷散列,那么我们就从布谷散列开始说它的设计思想。产生布谷散列表的一个重要背景是人们对于球盒问题的分析:给定N个球,随机的放在N个盒子里,在装球最多的盒子里,球的个数的期望是多少?答案是\(\Theta (logN/loglogN)\),这个问题其实就是散列表装填因子为1时的情况分析。后来有一天,人们发现:每次放球的时候,如果随机选择两个盒子,将球放到当时较空的那个盒子里,那么这个期望就变成了\(\Theta (loglogN)\),这个界小于之前的界,这给了布谷散列表作者启发。
一个布谷散列表通常有两张(一般来说)表,分别有一个对应的Hash函数,当有新的项插入的时候,它会计算出这个项在两张表中对应的两个位置,这个项一定会被存在这两个位置之一,而具体是哪一个却不确定。
下图是一个布谷散列表的初始化示意图:
现在我们假设有一些项要存入散列表,其每个项都有其对应的两个位置,先插入第一项A
由于插入A的时候其两个候选位置(0,2)都没有占用,所以选择第一张表或者是第二张表都可以,我们在这里默认先选择第一张表,然后插入第二项B
我们看到原来的A的位置被B占用,而A被“踢”到它的备选位置表二的2号位置上了,这就是当发生位置冲突时,布谷散列表的处理逻辑,后来的数据项将会把之前占用的项踢到另一个位置上。我们接下来插入第三项C
没有冲突,顺利搞定,接着插入D
D成功的把C踢走了,其实看到这里读者应该在猜想,会不会有一种情况,即被踢走的数据的另一个备选位置也被占用了,这样怎么办?答案是继续踢,一个踢一个,直到大家都找到自己合适的归宿为止。那么如果发现出现了循环怎么办?答案是GG,这代表布谷散列表走到了极限
插入E
这里就发生了多次替换的情况,F代替了E,E代替了A,A代替了B,B找到了空余的位子
读者可以考虑一下,如果这个时候要想插入一个“G”,其备选位置是1,2,这样的话会出现什么情况?
好了,布谷散列表大概介绍完了,现在该布谷过滤器了。布谷过滤器的主要也是通过布谷散列来实现的,其主要变化是:
1.我们不将原来的数据完整的存进来,作为过滤器,当然要省点空间,选用的办法设计一个指纹,将比较大的原数据变成了一个个指纹串,这样就大大节省了空间。
2.由于第一点所说,存储的不是原数据,那么在替换位置的时候,我们需要再次计算原来的数据的备选位置,这样一来布谷散列表的方法就失效了。在这里,作者设计了一个方法,他将两个Hash函数变成了一个Hash函数,第一张表的备选位置是Hash(x),第二张表的备选位置是\(Hash(x) \oplus hash(fingerprint(x))\),即第一张表的位置与存储的指纹的Hash值做异或运算。这样做的优点就是不用再另外存储元素的备选位置,在替换时,可以直接用异或来计算出其另一个备选位置。(读者可以想想如何通过表二的位置计算出元素在表一中的位置)
3.插入时,优先选择空位置,而不是尽可能的踢走其他元素。
插入伪代码如下:
Algorithm 1: Insert(x)
f = fingerprint(x)
i1 = hash(x)
i2 = i1 xor hash(f)
if bucket[i1] or bucket[i2] has an empty entry then
//只要有空位就先插入空位里
add f to that bucket
return Done
i = randomly pick i1 or i2
for n=0;n<MaxNumKicks;++n
randomly select an entry e from bucket[i];
swap f and the fingerprint stored in entry e;
i = i xor hash(f)
if bucket[i] has an empty entry then
add f to bucket[i]
return Done
return Failure // 已经出现循环情况了
查找伪代码如下:
Algorithm 2: Lookup(x)
f = fingerprint(x)
i1 = hash(x)
i2 = i1 xor hash(f)
if bucket[i1] or bucket[i2] has f then
return True
return False
删除伪代码如下:
Algorithm 3: Delete(x)
f = fingerprint(x)
i1 = hash(x)
i2 = i1 xor hash(f)
if bucket[i1] or bucket[i2] has f then
remove a copy of f from this bucket
return False
删除这部分值得注意,当被删除元素的另一个备选位置有其他元素的指纹的时候,我们不能确定到底哪一个是要删除的元素,其实我们也不去关心到底是不是要删除的元素,我们直接删除掉其中的一个。这样一来,我们其实并没有真正的删除掉元素,为什么这么说,因为当你删除掉这个元素的时候我们再查找这个元素,按照算法来看我们还是一样能检索出来这个元素在我们的布谷过滤器里,这就是假阳率的其中一个来源(还有一个重要来源是指纹构造的重复,即多个元素产生相同指纹)
下面我们来分析一下布谷过滤器的平均每个元素占用的比特数,设每个桶里装\(b\)个指纹,要求错误率的上界为\(\epsilon\),\(f\)为指纹长度。
错误率的上界 = \(1-(1-1/2^f)^{2b} \approx 2b/2^f\)
那么这个上界要求小于要求的上界,即\(2b/2^f \le \epsilon\),得到
\(f \ge log_2^{2b/\epsilon} = log_2^{1/\epsilon} + log_2^{2b}\)
则平均每个元素占用的比特数为\(C \le (log_2^{1/\epsilon} + log_2^{2b}) / \alpha\)
在原论文中,作者其实后面还做了一个比较强行的优化,在此不提,后面设计其他过滤器的作者也没有把这个优化算数。。。。不过作者提到了在实际测试中,他们发现当b=4的时候是空间性能最好的情况,所以一般说来,我们直接把b当做常数4,代入到前面算出来的公式中,\(C \le (log_2^{1/\epsilon} + 3) / \alpha\)
布谷过滤器就说到这,布谷过滤器在错误率小于3%的时候空间性能是优于布隆过滤器的,而这个条件在实际应用中常常满足,所以一般来说它的空间性能是要优于布隆过滤器的。而且,布谷过滤器按照普通设计,只有两个桶,在查找的时候可以确保两次访存就可以做完,相比于布隆过滤器的K个Hash函数K次访存,在数据量很大不能全部装载在内存中的情况下,多一次访存那么时间上就输了。不过说完优点,布谷过滤器也有其相应的缺点,当装填因子较高的时候,容易出现循环的问题,即插入失败的情况,到这时就很难办。另外,它还有跟布隆过滤器共有的一个缺点,就是访问空间地址不连续,通常可以认为是随机的,这样严重破坏了程序局部性,对于Cache流水线来说非常不利,这为之后的过滤器设计埋下了一个伏笔。
过滤器系列(二)—— Cuckoo filter的更多相关文章
- WEB API 系列(二) Filter的使用以及执行顺序
在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理.引入了这一机制可以更好地践行DRY(Don’t Repeat Yourself)思想 ...
- Web API系列(二) Filter的使用以及执行顺序
在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行过程拦截处理.引入了这一机制可以更好地践行DRY(Don’t Repeat Yourself)思想 ...
- Wireshark入门与进阶系列(二)
摘自http://blog.csdn.net/howeverpf/article/details/40743705 Wireshark入门与进阶系列(二) “君子生非异也,善假于物也”---荀子 本文 ...
- Mina 系列(二)之基础
Mina 系列(二)之基础 Mina 使用起来多么简洁方便呀,就是不具备 Java NIO 的基础,只要了解 Mina 常用的 API,就可以灵活使用并完成应用开发. 1. Mina 概述 首先,看 ...
- struts2官方 中文教程 系列二:Hello World项目
先贴个本帖的地址,免得其它网站被爬去了struts2入门系列二之Hello World 即 http://www.cnblogs.com/linghaoxinpian/p/6898779.html ...
- Redis总结(五)缓存雪崩和缓存穿透等问题 Web API系列(三)统一异常处理 C#总结(一)AutoResetEvent的使用介绍(用AutoResetEvent实现同步) C#总结(二)事件Event 介绍总结 C#总结(三)DataGridView增加全选列 Web API系列(二)接口安全和参数校验 RabbitMQ学习系列(六): RabbitMQ 高可用集群
Redis总结(五)缓存雪崩和缓存穿透等问题 前面讲过一些redis 缓存的使用和数据持久化.感兴趣的朋友可以看看之前的文章,http://www.cnblogs.com/zhangweizhon ...
- 爬虫系列(二) Chrome抓包分析
在这篇文章中,我们将尝试使用直观的网页分析工具(Chrome 开发者工具)对网页进行抓包分析,更加深入的了解网络爬虫的本质与内涵 1.测试环境 浏览器:Chrome 浏览器 浏览器版本:67.0.33 ...
- Tomcat 内存马(二)Filter型
一.Tomcat处理请求 在前一个章节讲到,tomcat在处理请求时候,首先会经过连接器Coyote把request对象转换成ServletRequest后,传递给Catalina进行处理. 在Cat ...
- 图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二)
项目链接:https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1 欢迎fork欢迎三连!文章篇幅有限, ...
- 前端构建大法 Gulp 系列 (二):为什么选择gulp
系列目录 前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gul ...
随机推荐
- Zabbix邮件告警提示Couldn't resolve host name解决办法
zabbix设置好邮件告警,当有触发时邮件未发送 查看zabbix server日志,提示不能连接邮件发送服务器,但是ping是可以通的
- codeforces#505--B Weakened Common Divisor
B. Weakened Common Divisor time limit per test 1.5 seconds memory limit per test 256 megabytes input ...
- CentOS下LVM逻辑卷管理技术解释
1.LVM逻辑卷管理技术产生的背景 企业日益变化的存储需要使得传统的磁盘分区存储显得不够灵活 2.磁盘分区存储 对于这样的三个物理分区的话,迟早有一天会被数据填满,因为它是死的,无法进行缩放. 假设下 ...
- django-mvc
而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序.服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理.应用程序则负责具体的逻辑处 ...
- python获取当天日期进行格式转换
# Python Library import time def getToday(format=3): """返回今天的日期字串""" # ...
- Mybatis三剑客之mybatis-generator配置
mybatis插件在这里: 然后把generatorConfig.xml文件放在resources下: <?xml version="1.0" encoding=" ...
- Flask系列之蓝图中使用动态URL前缀
让我们先来看一个简单的例子,假设有下面这样一个蓝图(是关于用户主页的): from flask import Blueprint, render_template profile = Blueprin ...
- sql用法
1: SELECT `SCHEMA_NAME` FROM `information_schema`.`SCHEMATA`; 查询sql中的数据库名 2: select * from for ...
- tar命令解压时如何去除目录结构及其解压到指定目录 (--strip-components N)
去除目录结构加上 --strip-components N 如: 压缩文件eg.tar 中文件信息为 src/src/src/eg.txt 运行 tar -xvf eg.tar --strip-com ...
- array2xml xml2array
array2xml/** * * 将简单数组转化为简单的xml * @param string $data 要进行转化的数组 * @param string $tag ...