哈希函数的应用场景
本文主要介绍哈希函数的常见应用场景,包括散列表、布隆过滤器以及分布式一致性哈希。
散列表(哈希表)
哈希函数的性质
哈希函数是一种映射函数Hash(key)=addr
通过对哈希地址进一步映射(通常是取余),得到相对应的索引
思考:通过对哈希地址进一步映射可以看作是进一步的哈希函数,例如(hash2(key)=hash1(key)%size)
哈希函数可能使不同的key映射到同一个地址(索引),这种情况称为哈希冲突
哈希函数可能使不同的key得到相同的索引(可能是相同或不同的哈希地址),这种情况成为哈希冲突
注明:哈希函数的定义有点混乱,有人说得到哈希码是哈希函数,有人说得到索引值是哈希函数,这边采用第一种定义了
选择哈希函数
- 计算速度快
- 强随机分布性
- 通常选择murmurhash2、siphash、cityhash
负载因子
用于形容散列表的存储密度,计算方式为数据存储元素的个数/数据长度;负载因子越小,冲突越小。负载因子越大,冲突越大。
散列表数据存储
散列表通常使用一个数组来存储元素,在没有哈希冲突的情况下,便将数据存放在数组索引为hash(key)%size的地方
解决散列表中的哈希冲突
负载因子在合理范围内(0.1size<used<size):
- 链表法
- 开放寻址法
负载因子不在合理范围内(used<0.1size)或(used>size):
- 缩容或者扩容,然后进行再哈希
散列表的应用
STL容器中:
- unordered_set
- unordered_map
- unordered_multiset
- unordered_multimap
与此同时,为了实现迭代器,同时将结点串成一个单链表
散列表与平衡二叉树对比:
平衡二叉树通过对key值比较,结构有序来提升搜索效率
散列表得到key与存储结点位置的映射关系
布隆过滤器
背景
内存有限,只需要确定某个key存不存在,而不需要具体的数据。
布隆过滤器与散列表对比
散列表通常使用数组存储元素,并只有一个哈希函数
布隆过滤器通常使用位图存储元素,并有多个哈希函数
布隆过滤器数据存储
位图
多个哈希函数
由上图可得布隆过滤器不支持删除操作
判断数据(key)存在
当通过哈希函数找到索引位置时
- 只要有一个为0,就一定不存在
- 如果都为1,不一定存在,但可以对概率(假阳率)进行控制
布隆过滤器参数配置
使用布隆过滤器时,往往先确定n和p,进而得到m和k
分布式一致性哈希
用途
解决分布式缓存扩容的问题(缓存失效)
改进
改进1
如下图所示,对结点和键值都使用哈希函数,并且取相同的模,称为固定算法。采用顺时针查找(例如k1放到192.168.1.101)。当这时添加一个新的结点时(192.168.1.103),只有在192.168.1.102与192.168.1.103映射的索引这段范围的键值会发生缓存失效,只是局部的缓存失效。这时,要将这段范围存储的键值与数据从192.168.1.100迁移到192.168.1.103,称为哈希迁移。这样便可以避免缓存失效了。
改进2
上述方法有个问题,当结点数较少时,可能有点结点会存储大量数据而有的结点存储少量的数据,相对应的哈希迁移数据量也差异较大,称为哈希偏移
通过增加虚拟结点,可以解决哈希偏移问题,如下图所示
总结
分布式一致性哈希通过固定算法、数据迁移避免了缓存失效
分布式一致性哈希通过虚拟结点保证数据均衡