• 海量数据查找与布隆过滤器


    背景:
    1. 在使用word文档时,word如何判断某个单词是否拼写正确?
    2. 网络爬虫程序,怎么让它不去爬相同的url页面?
    3. 垃圾邮件(短信)过滤算法如何设计?
    4. 公安办案时,如何判断嫌疑人是否在网逃名单中?
    5. 缓存穿透问题如何解决?
     
     
    缓存穿透
     
     
    mysql数据库,为了快速索引,减少磁盘IO,使用b+树;mysql中可能有缓冲池,查询缓存。但是,server向mysql请求数据过于频繁的话,mysql可能会承受不住。这时候需要引入缓存数据库redis,redis为了减少server访问db的压力。
     
    缓存穿透:
    1. 请求数据的时候:server redis mysql。
    2. 数据访问步骤:
      1. 先访问redis,若存在,直接返回;如果不存在
      2. 访问mysql,如果不存在,直接返回;如果存在,返回数据,且
      3. 将mysql存在的key写回到redis。
    3. 所谓穿透,就是redis和mysql都没有数据,黑客利用这个漏洞,不断请求这个数据,导致数据压力堆积在mysql中,最终导致系统崩溃。
     
    解决方案
    1. 在redis端设置<key, null>键值对,以此避免访问mysql;缺点是<key, null>过多的话,会占用过多的内存
      1. 可以设置key过期,这样redis就不会存在过多的数据,黑客停止攻击之后,这些key会被淘汰。
    2. 在server端存储一个布隆过滤器,将mysql包含的key放入布隆过滤器中;布隆过滤器一定能过滤不存在的数据。即利用这个数据结构快速查询数据是否在mysql中。
     
    主题:用字符串去海量字符串库查询是否存在?
    1. set,map和unordered_map
    2. set和map用红黑树实现的,unordered_map用哈希表实现。
      1. set和map关键区别,set不存储val字段。
    3. 红黑树全称为平衡二叉搜索树,不是严格的平衡二叉搜索树
      1. 红黑树时间复杂度是O(logn)
      2. 查找的时候,最差的情况是树的高度
      3. 红黑树的最长路径是最短路径的两倍
      4. 红黑树能不能插入相同的节点,看实现,可以实现为插入或者更新,或者直接丢弃
      5. 红黑树优点:存储效率高,访问速度高效。例如当int作为key时。
      6. 缺点:对于数据量大且查询字符串比较长且查询字符串相似时将会是噩梦。邮件或短信作为key,比较慢;字符串相似时,耗时间。
    4. AVL树:是严格的平衡二叉树
      1. 时间复杂度接近O(logn)
     
    例子
    • 100w条数据组成的红黑树,只需要比较20次就能找到该值;对于10亿条数据,只需要比较30次,就能找到该数据。
    • 问题:比较20,30次,如果是垃圾邮件,垃圾短信过滤,这种长字符串,比较二三十次,效率也很低,所以不适用于红黑树。
     
    unordered_map
    1. STL中的unordered_map使用hashtable实现的。
    2. 构成:数组+hash函数
    3. 它是将字符串通过hash函数生成一个整数,再映射到数组当中;增删改查的时间复杂度是O(1);
    4. hash函数的作用:避免插入的时候字符串的比较
    5. 如何选取hash函数:
      1. 计算速度快
      2. 相似字符串能保持强随机分布(放碰撞);
      3. 例如Murmurhash2, siphash, cityhash函数。
    6. 负载因子:数组存储元素的个数:负载因子越小,冲突越大;负载因子越大,冲突越小;
      1. 负载因子<1最好
    7. hash冲突解决方案:
      1. 链表法:将冲突的元素,用链表的方式连接起来;极端情况,链表过长,为了减少比较次数,可以将这个链表转换为红黑树(JAVA 8)。经验值:超过256或512的时候转换为红黑树。redis 6.0,对内存有严格的限制,使用链表发,并且是头插法。
      2. 开放地址法:所有元素都存在于哈希表中,不使用额外的数据结构。一般使用线性探测的思路解决冲突:
        1. 当插入元素时,使用hash函数在哈希表中定位元素位置;
        2. 检查数组中该槽位是否存储元素。如果该槽位为空,则插入,否则3;
        3. 在2检测的槽位索引上加上一定的步长,接着检查2;加步长有如下方法:
          1. i + 1, i + 2, i + 3, ...
          2. i - 1^2, i + 2^2, i - 3^3, ...
          3. 这两种都会导致hash聚集,也就是近似值它的哈希值也相似。第二种只是将hash冲突延后,没有根治。
          4. 解决办法:使用双重hash(.net里面)。利用互质传递性,减少碰撞。
    8. hash表同样存储了key和value,key没有顺序。同样可以修改代码,让插入数据变成修改操作。
    9. 如果数组长度过小,导致链表长度过长,可以一:将链表改为红黑树 二:rehash
    10. 优点:访问速度更快;不需要进程字符串比较
    11. 缺点:需要引入策略避免冲突,存储效率不高;空间换时间。
     
    问题:set,map和unordered_map都需要存储key值。如果是垃圾邮件,key过大,需要存储key。海量数据下,没有那么大内存。
    解决访问:使用布隆过滤器。布隆过滤器不用存储key就可以知道数据是否存在。
     
    布隆过滤器
    1. 定义:布隆过滤器是一种概率型数据结构(跳表也是),它的特点是高效的插入和查询,能明确告知某个字符串一定不存在或可能存在。适用于误差不是零容忍的情况。
    2. 布隆过滤器相比于传统的查询结构(hash, set, map等结构)更加高效,占用空间更小;但缺点是它的结果是概率性的,也就是说结果存在误差,虽然这个误差是可控的;同时它不支持删除操作。
    3. 组成:位图(bit) + n个hash函数。在C++中可以使用byte b[8]来构建64bit的位图。
    4. 原理:当一个元素加入位图时,通过k个hash函数将这个元素映射到位图的k个点,并把它们置为1;当检索时,再通过k个hash函数检测位图的k个点是否都为1;如果有不为1的点,那么认为不存在;如果全部为1,则可能存在(存在误差)。
    5. 在位图中每个槽位只有两种状态(0或1),一个槽位被设置为1状态,但不明确它被设置了多少次;也就是不知道被多少个str哈希映射以及是被哪个hash函数映射过来的;所以不支持删除操作。
    6. 在实际应用过程中,布隆过滤器如何使用?要选择多少个hash函数,要分配多少空间的位图,存储多少元素?另外如何控制假阳率?Bloom Filter Calculator
    1. 已知k,如何选择k个hash函数?
    // 采用一个hash函数,给hash传不同的种子偏移 // #define MIX_UNIT64(v) ((uint32_t)((v>>32)^(32))) uint64_t hash1 = MurmurHash2_64(key, len, Seed); uint64_t hash2 = MurmurHash2_x64(key, MIX_UINT64(hash1)); for (i = 0; i < k; i++) // k是hash函数的个数 { Pos[i] = (hash1 + i * hash2) %m; // m是位图的大小 } //通过这种方式模拟k个hash函数 和前面开放寻址法 双重hash是一样的思路
     
    附:
     

  • 相关阅读:
    三种常见的编码:ASCII码、UTF-8编码、Unicode编码等字符占领的字节数
    快学Scala习题解答—第十章 特质
    [LeedCode OJ]#63 Unique Paths II
    收集互联网博客
    Android 内存监测工具 DDMS --> Heap(转)
    (转载)测试工具monkey
    (转载)Git使用教程
    时间戳转换为时间字符串
    二维码相关知识点
    教你跳转到系统指定页面
  • 原文地址:https://www.cnblogs.com/ZGreMount/p/15503400.html
Copyright © 2020-2023  润新知