海量数据去重的hash,BloomFilter,bitmap


一、海量数据

背景

使用 word 文档时,word 如何判断某个单词是否拼写正确?
网络爬虫程序,怎么让它不去爬相同的 url 页面?
垃圾邮件过滤算法如何设计?
公安办案时,如何判断某嫌疑人是否在网逃名单中?
缓存穿透问题如何解决?

我们这里定一个需求叫做海量数据中查询某个字符串是否存在

平衡二叉树

增删改查时间复杂度为O(logn) ;
平衡的目的是增删改后,保证下次搜索能稳定排除一半的数据;
O(logn)的直观理解:100万个节点,最多比较 20 次;10 亿个节点,最多比较 30 次;
那么用这种方法是否可以拿来从海量数据当中查询字符串是否存在呢
有两个问题我们不能忽视
第一个,我们需要存储具体的元素,第二个,比较字符串
这两种不太让人接受,所以这里我们放弃这个方案
总结:通过比较保证有序,通过每次排除一半的元素达到快速索引的目的;
在这里插入图片描述

散列表

根据 key 计算 key 在表中的位置的数据结构;是 key 和其所在存储地址的映射关系;
注意:散列表的节点中 kv 是存储在一起的;

struct node { 
	void *key; 
	void *val; 
	struct node *next;
};

在这里插入图片描述

hash函数

地址映射
映射函数 Hash(key)=addr ;hash 函数可能会把两个或两个以上的不同 key 映射到同一地址,这种情况称之为冲突(或者 hash 碰撞);

选择hash

计算速度快
强随机分布(等概率、均匀地分布在整个地址空间)
murmurhash1,murmurhash2,murmurhash3,siphash(redis6.0当中使⽤,rust等大多数语言选用的hash算法来实现hashmap),cityhash 都具备强随机分布性;测试地址如下:
https://github.com/aappleby/smhasher
siphash 主要解决字符串接近的强随机分布性 ;

负载因子

数组存储元素的个数 / 数据长度;用来形容散列表的存储密度;负载因子越小,冲突越小,负载因子越大,冲突越大;

冲突处理

链表法:

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

开放寻址法

将所有的元素都存放在哈希表的数组中,不使用额外的数据结构;一般使用线性探查的思路解决;
1、当插入新元素的时,使用哈希函数在哈希表中定位元素位置;
2、检查数组中该槽位索引是否存在元素。如果该槽位为空,则插⼊,否则3;
3、在 2 检测的槽位索引上加一定步长接着检查2;
在这里插入图片描述

双重哈希解决出现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;
那么散列表可以用作查询海量数据当中的字符串吗
散列表不用去比较元素,但是和平衡二叉树一样需要存具体的元素
那么我们怎么去实现,不比较字符串,不存储具体元素
我们引入了布隆过滤器

二、布隆过滤器

布隆过滤器不存储具体元素,吸收了散列表的优点,通过hash的方式去算出具体的值,不用去比较。
布隆过滤器有多个hash函数,就需要去使用双重hash解决hash聚集现象
布隆过滤器是一种概率型数据结构,它的特点是高效地插入和查询,能确定某个字符串一定不存在或者可能存在;
布隆过滤器不存储具体数据,所以占用空间小,查询结果存在误差,但是误差可控,同时不支持删除操作;
在这里插入图片描述在这里插入图片描述

原理

当一个元素加入位图时,通过 k 个 hash 函数将这个元素映射到位图的 k 个点,并把它们置为 1;
当检索时,再通过 k 个 hash 函数运算检测位图的 k 个点是否都为 1;如果有不为 1 的点,那么认为该 key 不存在;如果全部为 1,则可能存在;
为什么不支持删除操作?
在位图中每个槽位只有两种状态(0 或者 1),一个槽位被设置为 1 状态,但不确定它被设置了多少次;也就是不知道被多少个 key 哈希映射而来以及是被具体哪个 hash 函数映射而来;
因为这个两个不同的元素,映射到的位,可能存在一样的,所以之前提到的布隆过滤器确定一个字符串是可能存在而不是绝对存在。不能确定这个位的1是哪个元素映射出来的

在这里插入图片描述

应用场景

布隆过滤器通常用于判断某个 key 一定不存在的场景,同时允许判断存在时有误差的情况;
常见处理场景:① 缓存穿透的解决;② 热 key 限流;
这个热key限流意思是说,当一个数据访问次数过多的时候,这个数据设为key,在某个时间段去统计这个数据的访问次数,次数达到一定的阈值,就会把这个key放到布隆过滤器当中,下一次来访问这个key的时候,先访问这个布隆过滤器,是否是在这个热key当中,不在布隆过滤器当中就直接访问,如果在,存在误差直接操作失败,这就是快手当中处理kv当中的一个热key限流的逻辑。
在这里插入图片描述描述缓存场景,为了减轻数据库(mysql)的访问压力,在 server 端与数据库(mysql)之间加入缓存用来存储热点数据;描述缓存穿透,server端请求数据时,缓存和数据库都不包含该数据,最终请求压力全部涌向数据库;
数据请求步骤,如图中 2 所示;
发生原因:黑客利用漏洞伪造数据攻击或者内部业务 bug 造成大量重复请求不存在的数据;
解决方案:如图中 3 所示;

应用分析

在实际应用中,该选择多少个 hash 函数?要分配多少空间的位图?预期存储多少元素?如何控制误差?
如果str通过hash都是映射到不同的位置
公式如下:
在这里插入图片描述

确定 n 和 p

在实际使用布隆过滤器时,首先需要确定 n 和 p,通过上面的运算得出 m 和 k;通常可以在下面
这个网站上选出合适的值
https://hur.st/bloomfilter
变量关系
假定4个初始值:
n = 4000 p = 0.000000001 m = 172532 k = 30
百度曾经问过一个面试题,hash函数实现过程中为什么会出现i31
需要考虑这几种种情况
i
31=i*(32-1)=i*(1<<5-1)=i<<5-i
31是质数,hash经常把这种质数作为取余的mod,hash函数会更加地均匀。
hash的随机分布性最好。

选择 hash 函数

选择一个 hash 函数,通过给 hash 传递不同的种子偏移值,采用线性探寻的方式构造多个 hash函数;

#define MIX_UINT64(v) ((uint32_t)((v>>32)^(v))) 
uint64_t hash1 = MurmurHash2_x64(key, len, Seed); 
uint64_t hash2 = MurmurHash2_x64(key, len,MIX_UINT64(hash1)); for (i = 0; i < k; i++) // k 是hash函数的个数 
{ 
	Pos[i] = (hash1 + i*hash2) % m;  // m 是位图的⼤⼩ 
} 

三、分布式一致性 hash

分布式一致性 hash 算法将哈希空间组织成一个虚拟的圆环,圆环的大小是2的32次方;
算法为: hash(ip)%2的32次方 ,最终会得到一个 [0, 2的32次方减一] 之间的一个无符号整型,这个整数代表服务器的编号;多个服务器都通过这种方式在 hash 环上映射一个点来标识该服务器的位置;当用户操作某个 key,通过同样的算法生成一个值,沿环顺时针定位某个服务器,那么该 key 就在该服务器中;图片来源于互联网;
分布式缓存为什么需要hash呢
缓存本身没有带这种集群操作的,就是那种横向扩展集群
我们的服务器需要把数据均衡地发送给多个memcache当中去
,最开始呢,我们采用hash的方式,hash首先对这个key进行取值,hash(key)%3, 3是集群数量。就是把数据配到3个不同的ip上去,等我们把这个服务器部署好了以后,发现当数据不断增加的时候,我们就要考虑增加节点,增加节点问题就出现了,这个hash运算就改变了就变成hash(key)%4,这个数据传送就乱套了。
在这里插入图片描述

我们接着就想到了一种方式叫一致性hash,什么叫一致性hash呢,把这个hash算法固定住,hash(key)%2的32次方。
2的32次方把它想成一个hash循环,相当于一个循环数组,就是0到2的32次方减一。
例如
hash(ip:port)%2的32次方,对设备进行操作
hash(key)%2的32次方,对数据进行存储
用顺时针查找
在这里插入图片描述
分布式一致性hash只造成一部分影响
hash偏移,造成数据存储不均衡,hash偏移的产生原因是节点太少了。在计算机当中凡是涉及到概率问题,一定是数量问题
解决方案是加虚拟节点,增加虚拟节点就要找到真实的ip
在这里插入图片描述

在这里插入图片描述
这个就解决了hash偏移的问题
分布式一致性hash造成的部分影响,这个影响如何消除呢,这里还有个数据迁移问题
我们参考这个:https://github.com/metang326/consistent_hashing_cpp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值