| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> C/C++数据结构之Hash与BloomFilter详解 -> 正文阅读 |
|
[数据结构与算法]C/C++数据结构之Hash与BloomFilter详解 |
海量数据去重的Hash与BloomFilter,bitmap平衡二叉树平衡二叉树查找数据采用二分查找,每次查找排除一半。平衡的目的是增删改之后,保证下次搜索能够稳定排除一半的数据。 平衡二叉树通过比较保证有序,通过每次排除一半的元素达到快速索引的目的。
二分查找
有序数组
平衡二叉树
平衡多路搜索树
多层级有序链表
调表
红黑树
AVL
B树
B+树
散列表在平衡二叉树中,搜索数据时总是对key进行比较,如果在海量数据中使用这种方式,搜索效率会很低。 散列表是一种不比较key,而是根据key计算key在表中的位置的数据结构;是key和其所在存储地址的映射关系。散列表通过此方式达到快速索引的目的。
散列表的构成(1)hash函数。 hash的选择hash的选择遵循两个原则: 注意,hash函数可能会把两个或两个以上的不同key映射到同一地址,这种现象称为hash冲突。 散列表操作流程散列表的插入操作和搜索操作都要经过hash函数找到key对应的存储地址。首先,key经过hash函数hash(key)等到一个64bit/32bit的值maddr;然后maddr对数组长度取余,得到的值就是存储节点的位置。
指针数组
0
1
2
3
4
5
6
7
node
node
node
node
node
冲突冲突产生原因在数组大小不变情况下,随着数据的越来越多,必然产生冲突;而且hash是随机性的,这也可能产生冲突。 负载因子负载因子=数组存储元素的个数 / 数组长度; 负载因子用于描述冲突的激烈程度和存储的密度。负载因子越小,冲突概率越小,负载因子越大,冲突概率越大。 冲突处理解决冲突的方法有很多,比较常用的有:链表法 / 拉链法、开放寻址法等。 链表法链表法是常用的处理冲突的方式。通过引用链表来处理hash冲突,即将冲突元素用链表链接起来。
指针数组
0
1
2
3
4
5
6
7
node
node
node
node
node
开放寻址法开放寻址法将所有的元素都存放在哈希表的数组中,不使用额外的数据结构。一般使用线性探查的的思路解决: 扩容和缩容以上两种解决冲突的方式都是负载因子在合理范围内。当负载因子不在合理范围内,如何处理? (2)当used < 0.1 * size,即要使用的空间远远小于数组的空间,这时就需要缩容;缩容不能解决冲突,只能节约空间,减少内存浪费。
扩容
rehash
缩容
旧数组的key重新映射,搬到新数组中,释放旧数组的内存
STL unordered_* 散列表实现在 STL 中 unordered_map、unordered_set、unordered_multimap、unordered_multiset 四个容器的底层实现都是散列表。
STL
红黑树
散列表
set
map
multiset
multimap
unorder_set
unorder_map
unorder_multiset
unorder_multimap
原理图:
指针数组
0
1
2
3
4
5
6
7
8
_hash_node_base* _M_before_begin
_hash_node_base*
_hash_node_base*
_hash_node_base*
_hash_node_base*
_hash_node_base*
_hash_node_base*
一般,hash table里面的槽位单独通过链表串联所属槽位的数据;STL散列表的槽位指针不再这么做,做了优化,将后面具体结点串成一个单链表,而槽位指针指向上一的结点。 举个例子: 布隆过滤器背景无论是红黑树、平衡二叉树、散列表,结点都是存储的key-value对。而有些场景,内存是有限的,仅需要了解key是否存在,不想知道具体内容(value)。 这时就需要布隆过滤器。布隆过滤器是一种概率型数据结构,它的特点是高效的插入和查询,能确定某个字符串一定存在或者可能存在。 (1)一个巨大的数据文件,需要知道是否存在某个key,如果把整个文件读取进行查找,这个效率就比较低。那么可以添加一个布隆过滤器,插入数据时对key做标识,查询key是否存在时直接查询布隆过滤器。 构成布隆过滤器的原理本质上和散列表是一样的。但布隆过滤器为了节约内存,不是使用的数组,而是使用的位图。
(2)n个hash函数 举例:
原理当一个元素加入位图时,通过k个hash函数将元素映射到位图的k个点,并把它们置1;当检索时,再通过k个hash函数运算检查位图的k个点是否都为1;如果有不为1的点,那么认为该key不存在;如果全部为1,则可能存在。 布隆过滤器是不支持删除操作的,原因在于:在位图中每个槽位只有两种状态(0或者1),一个槽位被置为1,但不确定它被设置了多少次;也不知道被多少个key hash映射而来;以及具体被哪个hash函数映射而来。 值得注意的是,只要有一个槽位为0,则key一定不存在;如果key映射的所有槽位都为1,不能说明一定存在,只能说明可能存在(假阳率)。
bitmap
0
0
1
0
0
1
0
0
1
0
1
1
hash函数
Hash1(key)
Hash2(key)
Hash3(key)
str1
str2
应用场景布隆过滤器通常用于判断某个key一定不存在的情况。同时允许判断存在时有误差的情况。 缓存场景:为了减轻数据库(MySQL)的访问压力,在数据库(MySQL)和服务端(server)直接加入缓存用来存储热点数据。 数据请求步骤:
key
Yes
No
key
NO
Yes
Server
redis
是否存在
return
MySQL
是否存在
key写回redis
发生原因:黑客利用漏洞伪造数据攻击或内部业务bug造成大量重复请求不存在的数据。 解决方案: 应用分析在实际应用中,该选择多少个 hash 函数?要分配多少空间的位图?预期存储多少元素?如何控制误差? 数学公式: 变量关系假设: 确定n和p在实际使用布隆过滤器时,首先需要确定 n 和 p,通过上面的运算得出 m 和 k;推荐一个布隆过滤器计算器可以选出合适的值。 选择hash函数选择一个 hash 函数,通过给 hash 传递不同的种子偏移值,采用线性探寻的方式构造多个 hash 函数;
分布式一致性hash背景假设一个服务器,只有一个缓存结点,当存储的数据越来越多时,效率就越来越低,这时就需要增加结点进行分流分压。那么如何实现优雅的扩容(数据随机、均匀分布)?首先想到使用hash方法解决,但扩容时(增加结点)会出现算法改变;比如原来有n个结点,key通过hash(key)%n确定存储在哪个结点,现在添加新的结点变成了n+1,key通过hash(key)%(n+1)寻找存储结点就会出现缓存失效。
扩容
扩容n=4
缓存失效
缓存失效
1号结点
2号结点
3号结点
0号结点
hash(key=1)%4=1
hash(key=2)%4=2
hash(key=3)%4=3
hash(key=4)%4=0
现在n=3
1号结点
2号结点
0号结点
hash(key=1)%3=1
hash(key=2)%3=2
hash(key=3)%3=0
hash(key=4)%3=1
分布式一致性hash就解决了缓存扩容的问题。分布式一致性hash算法将hash空间组织成一个虚拟的圆环,圆环大小为
2
32
2^{32}
232 。
服务器1
key1
服务器3
服务器0
key2
服务器2
一致性hash原理(1)映射空间可抽象为一个环,长度为 2 32 2^{32} 232,范围为[0, 2 32 ? 1 2^{32}-1 232?1],每个服务器节点根据自己的哈希值被映射到这个环上; (2)判断一条数据属于哪个服务器节点的方法:根据数据的哈希值,去哈希环找到第一个大于等于数据哈希值的机器(可以理解为离它最近)。如果数据的哈希值大于当前最大的机器哈希值,那么就把这个数据放在位置最靠前(哈希值最小)的机器上,因为是一个环。 (3)为了解决实际机器过少导致的数据倾斜问题(例如目前一共3个机器,机器A、B的哈希值分别为1和2,而另一个机器C的哈希值为 2^32-1,那么大部分的数据都会被分给机器C),引入了虚拟节点概念,虚拟节点相当于真实节点的分身,一个真实节点可以有很多个虚拟节点,当数据被分配给这些虚拟节点时,本质上是分给这个真实节点的。由于数量变多了,数据分布的均衡性会有所提高; (4)新增节点时:例如原本的节点哈希值列表为[1,100,500,1000],新增节点800后,在501~799范围内的数据原本是分给哈希值为1000的节点的,现在要把这部分数据迁移到节点800; (5)删除节点:例如原本的节点哈希值列表为[1,100,500,800,1000],删除节点500后,原本范围是101~500的数据要迁移到节点800. 应用场景(1)分布式缓存;将数据均衡地分散在不同的服务器当中,用来分摊缓存服务器的压力; hash偏移hash算法得到的结果是随机的,不能保证服务器节点均匀分布在hash环上;分布不均造成请求访问不均匀,服务器承受的压力不均匀。
服务器1
key1
key3
key4
key...
服务器3
服务器0
key2
服务器2
为了解决实际机器过少导致的数据倾斜问题(例如目前一共3个机器,机器A、B的哈希值分别为1和2,而另一个机器C的哈希值为 2^32-1,那么大部分的数据都会被分给机器C),引入了虚拟节点概念,虚拟节点相当于真实节点的分身,一个真实节点可以有很多个虚拟节点,当数据被分配给这些虚拟节点时,本质上是分给这个真实节点的。由于数量变多了,数据分布的均衡性会有所提高; hash迁移新增节点时,例如原本的节点哈希值列表为[1,100,500,1000],新增节点800后,在501~799范围内的数据原本是分给哈希值为1000的节点的,现在要把这部分数据迁移到节点800。 虚拟结点为了解决哈希偏移的问题,增加了虚拟节点的概念;理论上,哈希环上节点数越多,数据分布越均衡;为每个服务节点计算多个哈希节点(虚拟节点);通常做法 添加虚拟节点解决了哈希偏移的问题,同时使hash迁移的数据量变小。 思考(1)只用 2GB 内存在 20 亿个整数中找到出现次数最多的数?
key
key
拆分
0
1
2
3
4
5
6
7
8
9
20 亿个整数
hash(key) % 10
hash(key) % array_size
散列表
总结平衡二叉树通过比较,保证结构有序,从而提升搜索效率,稳定时间复杂度是 O ( log ? 2 n ) O(\log_2n) O(log2?n);而散列表是通过key与存储位置的映射关系来提高搜索效率,整个散列表是无序的。 散列表的组成主要有:hash函数、数组、运算流程(算法是hash(key) % array_size)。 散列表hash函数的作用是建立映射关系;选择hash函数要满足计算速度快、强随机分布的要求,比如murmurhash2、siphash、cityhash等使用比较广泛的hash算法。 解决hash冲突的方法有链表法、开放寻址法、扩容等;链表法中如果槽位的链表很长(超过256个元素),可以转换为红黑树或最小堆的数据结构,将时间复杂度由O(n)变为 O ( l o g 2 n ) O(log_2n) O(log2?n)。扩容、缩容后都需要重新hash,即重新映射。 散列表的操作流程都是需要经过同样的运算(映射),找到存储位置再插入。 STL的散列表实现中,为了实现迭代器,将后面具体结点串成一个单链表。 布隆过滤器由位图和n个hash函数构成。布隆过滤器的操作是一个key经过多个hash函数,然后对位图大小进行取余等到多个槽位并对应置为1。判断时只要有一个槽位为0就一定不存在该key。 布隆过滤器不支持删除操作,可以通过两个布隆过滤器解决(依然存在假阳率,但会低一些),添加放在第一个布隆过滤器,删除放在第二个布隆过滤器。即要判断key是否存在,首先检查第二个布隆过滤器是否删除过,如果删除过就往第一个布隆过滤器插入。 布隆过滤器根据n和p算出m和k,hash函数个数是利用开放寻址法来计算的。 在大数据中,涉及到大文件或海量数据的,解决方案都是通过hash将大文件拆分为小文件;涉及单台机器无法承受或处理不过来的问题,解决方案都是通过hash分流到多台机器;选择hash的原因是利用其强随机分布的特性,以及把相同的数据分配到相同的位置。 分布式一致性hash算法通过固定算法,改变查找结点的映射关系,数据迁移,避免缓存失效,解决分布式扩容的问题;通过虚拟节点的方式保证数据均衡。 后言本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接,详细查看详细的服务:C/C++服务器 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 21:27:24- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |