数据结构:布隆过滤器

目录

1、背景

2、从海量数据中查询某字符串是否存在

2.1、使用set和map

2.2、unordered_map

2.3、总结

3、布隆过滤器

3.1、原理

3.2、假阳率


​​​​​​​

1、背景

  • 在使⽤word⽂档时,word如何判断某个单词是否拼写正确?

    扫描二维码关注公众号,回复: 12992494 查看本文章
  • ⽹络爬⾍程序,怎么让它不去爬相同的url⻚⾯?允许有误差

  • 垃圾邮件(短信)过滤算法如何设计?允许有误差

  • 公安办案时,如何判断某嫌疑⼈是否在⽹逃名单中?控制误差 假阳率

  • 缓存穿透问题如何解决?允许有误差

1、读取步骤:

    1)先访问redis,如存在,直接返回;如不存在走2;

    2)访问mysql,如不存在,直接返回;若存在走3;

    3)将mysql存在的key写回redis; 

2、缓存穿透:

黑客频繁请求redis,mysql都没有的数据,导致mysql压力过大,如此以来整个系统将陷入瘫痪。

3、解决方案:

    1)在redis端设置<key,null>键值对,以此避免频繁访问mysql;缺点是<key,null>过多的话,占用过多内存;(redis端的<key,null>键值对可以设置过期时间,比如设置过期时间为500ms,就算频繁请求,也只是频繁和redis交互,500ms才能请求一次mysql

    2)在server端存储一个布隆过滤器,将mysql包含的key放入布隆过滤器中;布隆过滤器能过滤一定不存在的数据;

2、从海量数据中查询某字符串是否存在

2.1、使用set和map

c++标准库(STL)中的set和map结构都是采用红黑树实现的,它增删改查的时间复杂度是O(log_{2}n)

  • 对于严格平衡二叉搜索树(AVL),100w条数据组成的红黑树。只需要最多比较20次就能找到该值;对于10亿条数据只需要最多比较30次就能找到该数据;也就是查找次数跟树的高度是一致的;
  • 对于红黑树来说平衡的是黑节点高度,所以研究比较次数需要考虑树的高度差,最好情况某条树链路全是黑色节点,假设此时高度为h1,最差情况某条链路全是黑红节点间隔,此时树高度为2*h1;
  • 在红黑树中每一个节点都存储key和val字段,key是用来做比较的字段;红黑树并没有要求key字段唯一,在set和map实现过程中限制了key字段唯一。我们来看nginx的红黑树实现:

nginx源码地址:http://hg.nginx.org/nginx/file

nginx中关于ngx_rbtree_insert_value的实现位置

//insert操作中的一部分,执行完这个函数还需要检测插入节点后是否平衡(主要看他的父节点是否也是红色节点)
//temp:红黑树的根节点;node:待插入的节点  
void
ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
    ngx_rbtree_node_t *sentinel)
{
    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        p = (node->key < temp->key) ? &temp->left : &temp->right;

        if (*p == sentinel) {
            break;
        }

        temp = *p;
    }

    *p = node;
    node->parent = temp;
    node->left = sentinel;
    node->right = sentinel;
    ngx_rbt_red(node);
}

2.2、unordered_map

unordered_map是采用Hashtable实现的。

优点:访问速度更快;不需要进⾏字符串⽐较;

缺点:需要引⼊策略避免冲突,存储效率不⾼;空间换时间。

2.3、总结

红⿊树和hashtable都不能解决海量数据问题,它们都需要存储具体字符串,如果数据量⼤,提供 不了⼏百G的内存;所以需要尝试探寻不存储key的⽅案,并且拥有hashtable的优点(不需要⽐较 字符串);

3、布隆过滤器

3.1、原理

布隆过滤器是一种数据结构,比较巧妙的概率型数据结构,特点是高效地插入或查询,可以用来告诉你“某样东西一定不存在或可能存在”。

相比较于传统的List、Set、Map等数据结构,它更高效、占用空间更少,但是缺点是其返回的不是确定值,而是概率性的结果。

我么先来直观描述下什么叫布隆过滤器数据结构

布隆过滤器的原理是,当一个元素被加入集合时,通过key个Hash函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:

  • 如果这些点中存在0,则被检测元素一定不存在;
  • 如果都是1,则被检测元素可能存在(我们期望存在的概率是多少可以设置)。

假设有三个Hash函数来设计此布隆过滤器

Hash1(“bytedance”) = 0; Hash2(“bytedance”) = 3; Hash3(“bytedance”) = 8;

Hash1(“tencent”) = 1; Hash2(“tencent”) = 3; Hash3(“tencent”) = 5;

检索bytedance时,通过哈希计算出三个点位分别为038,然后查看发现038位置均为1,则布隆过滤器告诉我们bytedance可能存在。

而如果此时我们要检索的值huawei计算出来的哈希值为358。此时布隆过滤器告诉我们huawei”可能存在,但实质上huawei根本就不存在(误判率,假阳),而这就是布隆过滤器的缺点。

布隆过滤器不支持删除,因为在位图中每个槽位只有两种状态(0或者1),如果⼀个槽位被设置为1状态,但不明确它被设置了多少次;也就是不知道被多少个哈希映射以及是被哪个hash函数映射过来的;所以不⽀持删除操作;

3.2、假阳率

布隆过滤器能明确⼀定不存在,不能明确⼀定存在,那么存在的判断是有误差的,假阳率就是错误判断存在的概率。

n -- 布隆过滤器中元素的个数,如上图 只有"tencent"和"bytedance"两个元素 那么 n=2
p -- 假阳率,在0-1之间 0.000000
m -- 位图所占空间
k -- hash函数的个数
公式如下:
n = ceil(m / (-k / log(1 - exp(log(p) / k))))
p = pow(1 - exp(-k / (m / n)), k)
m = ceil((n * log(p)) / log(1 / pow(2, log(2))));
k = round((m / n) * log(2));

/*
round:四舍五入

ceil:向上取整

exp:以e为底的指数

pow:次方
*/

在实际应⽤中,我们确定n和p,通过上⾯的计算算出m和k;也可以在下面⽹站上选取合适的值 https://hur.st/bloomfilter/

假阳率p随着元素个数n增大至823后以后陡然增加 

假阳率p与位图空间大小m成反比。空间越大,冲突的概率越小

这张图可以看出来不是哈希函数使用越多,假阳率就越低,有个最优解,过了那个值之后假阳率就会又开始升高。其实也很好理解,你的哈希函数越多,一个值存进来置位为1的位越多,当哈希函数数量过了某个临界值时,冲突就会明显增加。

猜你喜欢

转载自blog.csdn.net/weixin_40179091/article/details/112521477