hash
hash的应用
- word 判断某个单词是否拼写正确
- 网络爬虫防止爬相同url
- 垃圾邮件过滤算法
- 缓存穿透问题
不难看出,这些问题都可以看做是基于一个字符串,查找它在大的字符串集中是否存在。
hash的实现
和平衡多路搜索树比较,hash是通过一个映射函数,通过key获得key的地址。可以表示为 hash(key) = addr。
选择一个hash函数要满足以下性质:
- 计算速度快
- 随机分布性强(等概率,均匀分布)
- murmurhash1, murmurhash2, murmurhash3 速度 2 > 3 > 1 质量 3>2>1,一般使用2; siphash(redis6), 主要解决字符串接近的强随机分布性;cityhash。
- 操作流程
struct node{
void* key;
void* val;
struct node* next;
};
插入: h(key)%size 获得 pos
hash冲突 :多个 key 计算的 pos 是同一个,描述冲突的激烈程度(负载因子),数组存储的元素个数/数组长度,解决冲突(链表法,把冲突用链表存放,合理范围 used < size,这里存放同一个key的value的单链表,在超过256的时候可以换成红黑树或者堆,开放寻址法,位置被使用就双重哈希解决hash聚集现象。),如果负载因子不在合理范围内,就要使用扩容(解决冲突)和缩容(节约空间),再 rehash 。
STL中 unordered_* 的实现(散列表实现)
map set multimap multiset 是用红黑树实现的,unordered_map unordered_set unordered_multimap unordered_multiset 是用散列表实现的
散列表 Hashtable
本质上,插入使用的是头插法,整个表是一个单链表方便实现迭代器。
BloomFilter
使用场景
内存有限,只想确定某个key存不存在,不需要value的值。
构成
位图(bit数组) + n 个hash函数
m%2n = m &(2n -1)
插入:在位图相应位置赋值 1
查找不存在: 有一个hash位置为0就不存在
布隆过滤器不支持删除操作,因为他不清楚这个1是被那个hash映射的,被多少hash映射的
所以,布隆过滤器 应用在 不用删除的,判断一个key是不是不存在的场景。判断一个key是不是存在也可以,但是要假阳率可控的条件下。
使用
四个参数
N 预期存多少个元素
P 允许的假阳率 10e-7就是一千万里面允许错一次
M 位图空间的大小
K hash函数的数量
问题:用2G内存在2e14个数中找到出现次数最多的数
存储 散列表 k 整数(四字节 -2147483648到2147483647) v 出现次数(uint32 -2147483648 到 2147483647)
一个 kv 8字节 2亿 个 8字节 就是 1.6G内存,20亿个数就是 16G内存
所以把 20 亿个数拆成 多 份,每份 < 2G,把相同的整数放到同一个文件中,可以用hash的强随机分布性来做这件事,使得每个文件中分布数量均匀。求每份中出现数最多的数,再将多个文件的最大次数做比较。
总结:大文件 hash 拆成小文件,单台机器 hash 分流到多台机器(强随机分布性和hash函数选择数据)
hash一致性(解决扩容后 映射变化)
因为扩容时,算法发生改变,缓存失效
解决方法:固定算法(还是有局部失效),再做hash数据迁移。样本数越大越稳定,样本数少不稳定,可以增加虚拟结点(来保证数据均衡),增加样本数,使得迁移数据量变少。