海量数据去重方案-set/map/布隆过滤器(bitmap)

背景

  1. 在使⽤word⽂档时, word如何判断某个单词是否拼写正确?
  2. ⽹络爬⾍程序,怎么让它不去爬相同的url⻚⾯?允许有误差
  3. 垃圾邮件(短信)过滤算法如何设计?允许有误差
  4. 公安办案时,如何判断某嫌疑⼈是否在⽹逃名单中?控制误差 假阳率
  5. (重点)缓存穿透问题如何解决?允许有误差

海量数据去重方案-set/map/布隆过滤器(bitmap)_第1张图片

需求

  • 从海量的数据中查询某个字符串是否存在

Set/Map

  • C++标准库(STL)中的set和map结构都是采⽤红⿊树实现的,它增删改查的时间复杂度是 O(log2n)
  • 对于严格平衡⼆叉搜索树(AVL), 100w条数据组成的红⿊树,只需要⽐较20次就能找到该值;对于10亿条数据只需要⽐较30次就能找到该数据;也就是查找次数跟树的⾼度是⼀致的;
  • 对于红⿊树来说平衡的是⿊节点⾼度,所以研究⽐较次数需要考虑树的⾼度差,最好情况某条树链
    路全是⿊节点,假设此时⾼度为h1,最差情况某条树链路全是⿊红节点间隔,那么此时树⾼度为2*h1;
  • 另外set和map的关键区别是set不存储val字段;
    优点:存储效率⾼,访问速度⾼效;
    缺点:对于数据量⼤且查询字符串⽐较⻓且查询字符串相似时将会是噩梦

unordered_map

  • C++标准库(STL)中的unordered_map是采⽤hashtable实现的;
  • 构成:数组+hash函数;它是将字符串通过hash函数⽣成⼀个整数再映射到数组当中;它增删改查的时间复杂度是o(1);
  • hash函数的作⽤: 避免插⼊的时候字符串的⽐较; hash函数计算出来的值通过对数组⻓度的取模能随机分布在数组当中;
  • hash函数⼀般返回的是64位整数,将多个⼤数映射到⼀个⼩数组中,必然会产⽣冲突;
  • 如何选取hash函数?
    1. 选取计算速度快;
    2. 哈希相似字符串能保持强随机分布性(防碰撞);
    murmurhash1, murmurhash2, murmurhash3, siphash(redis6.0当中使⽤, rust等⼤数
    语⾔选⽤的hash算法来实现hashmap, cityhash都具备强随机分布性;测试地址如下:
    https://github.com/aappleby/smhasher
  • 负载因⼦:数组存储元素的个数/数组⻓度;负载因⼦越⼩,冲突越⼩;负载因⼦越⼤,冲突越⼤;
  • 同样的hashtable中节点存储了key和val, hashtable并没有要求key的⼤⼩顺序,我们同样可以修
  • 改代码让插⼊存在的数据变成修改操作;
  • 优点:访问速度更快; 不需要进⾏字符串⽐较;
  • 缺点:需要引⼊策略避免冲突,存储效率不⾼;空间换时间;

hash冲突解决⽅案: 

  • 链表法

引⼊链表来处理哈希冲突;也就是将冲突元素⽤链表链接起来;这也是常⽤的处理冲突的⽅
式;但是可能出现⼀种极端情况,冲突元素⽐较多,该冲突链表过⻓,这个时候可以将这个链
表转换为红⿊树;由原来链表时间复杂度 o(n) 转换为红⿊树时间复杂度 ;那么判
断该链表过⻓的依据是多少?可以采⽤超过256(经验值)个节点的时候将链表结构转换为红
⿊树结构;

  • 开放寻址法

将所有的元素都存放在哈希表的数组中, 不使⽤额外的数据结构;⼀般使⽤线性探查的思路解
决;
1. 当插⼊新元素的时,使⽤哈希函数在哈希表中定位元素位置;
2. 检查数组中该槽位索引是否存在元素。如果该槽位为空,则插⼊,否则3;
3. 在 2 检测的槽位索引上加⼀定步⻓接着检查2;加⼀定步⻓分为以下⼏种:
1. i+1,i+2,i+3,i+4 ... i+n
2. i- ,i+ ,i- ,1+ ...
这两种都会导致同类hash聚集;也就是近似值它的hash值也近似,那么它的数组槽位也靠
近,形成hash聚集;第⼀种同类聚集冲突在前,第⼆种只是将聚集冲突延后;

  • 另外还可以使⽤双重哈希来解决上⾯出现hash聚集现象:
在.net HashTable类的hash函数Hk定义如下:
Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) %
(hashsize – 1)))] % hashsize
在此 (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1))) 与 hashsize
互为素数(两数互为素数表示两者没有共同的质因⼦);
执⾏了 hashsize 次探查后,哈希表中的每⼀个位置都有且只有⼀次被访问到,也就是
说,对于给定的 key,对哈希表中的同⼀位置不会同时使⽤ Hi 和 Hj;

总结

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

 布隆过滤器

  • 定义:布隆过滤器是⼀种概率型数据结构,它的特点是⾼效的插⼊和查询,能明确告知某个字符串⼀定不存在或者可能存在
  • 布隆过滤器相⽐传统的查询结构(例如: hash, set, map等数据结构) 更加⾼效,占⽤空间更⼩;但是其缺点是它返回的结果是概率性的,也就是说结果存在误差的,虽然这个误差是可控的;同时它不⽀持删除操作
  • 组成:位图(bit数组) + n个hash函数(一般是23个)

海量数据去重方案-set/map/布隆过滤器(bitmap)_第2张图片

 原理

当⼀个元素加⼊位图时,通过k个hash函数将这个元素映射到位图的k个点,并把它们置为
1;当检索时,再通过k个hash函数运算检测位图的k个点是否都为1;如果有不为1的点,那么认为
不存在;如果全部为1,则可能存在(存在误差);
 

海量数据去重方案-set/map/布隆过滤器(bitmap)_第3张图片

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

 在实际应⽤过程中,布隆过滤器该如何使⽤?要选择多少个hash函数,要分配多少空间的位图,存储多少元素?另外如何控制假阳率(布隆过滤器能明确⼀定不存在,不能明确⼀定存在,那么存在的判断是有误差的,假阳率就是错误判断存在的概率)?

n -- 布隆过滤器中元素的个数,如上图 只有str1和str2 两个元素 那么 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));

在实际应⽤中,我们确定n和p,通过上⾯的计算算出m和k;也可以在⽹站上选取合适的值:
Bloom filter calculator

源码下载:https://download.csdn.net/download/u012173846/75159777icon-default.png?t=LBL2https://download.csdn.net/download/u012173846/75159777

你可能感兴趣的:(哈希算法,算法)