数据按照下标随机访问数据的特性
键值+散列函数+散列值
- 散列函数得到的散列值是非负整数
- key1 == key2 : hash(key1)==hash(key2)
- key1 != key2: hash(key1) != hash(key2)
标题散列冲突:
1.开放寻址法
线性探测
某个数据经过散列后,发现存储位置已经被占用,从当前位置往后找,直至找到空闲位置。
删除时:不能直接删除,否则可能导致之后的数据查找截断。可以把该位置标为deleted
缺点:随着数据增多,复杂度可能退化为O(n)
优化:
二次探测: hash(key)+1^2, hash(key)+2^2…
双重探测: 用其他的多个散列函数做冲突避免
装载因子=填入表中的元素个数/散列表的长度
装载因子越大,冲突越多,散列表性能下降。
2.链表法
每个桶/槽 对应一条链表,所有散列值相同的元素放置到相同槽位的链表
插入和删除 时间复杂度 与链表长度k成正比,理论上散列均匀的散列表k=n/m
word如何实现拼写检查
把所有的单词存在散列表
20w单词,每个单词长度平均10个字母,一个单词10byte内存,20w单词2MB存储
工业级散列表
设计散列函数
- 设计不能过于复杂,过于复杂的散裂函数,消耗计算时间,影响性能
- 散列值尽可能随机且均匀分布,避免最小化散列冲突
扩容
数组扩容:数据迁移
散列表扩容:数据迁移&数据存储位置改变,需要重新计算数据的位置。
一次性扩容,极度低效。
解决:将扩容穿插在插入操作中,分批完成。有新数据插入时,将新数据插入新散列表中,并从老的散列表中取一个数据置于新表。
冲突避免
1.开放寻址法
缺点:删除数据麻烦,需要特殊标记;数据存储在数组中,冲突代价高
数据量小,装载因子小,推荐开放寻址法。
2.链表法
缺点:存指针,对于小的对象的存储,消耗内存;链表结点零散分布,CPU缓存不友好
将链表改成跳表,红黑树的形式,出现散列冲突,查找时间也不超过O(logn)
适合存储大对象,大数据量散列表;更灵活,支持红黑树等优化策略。
JAVA HashMap 拉链过长,链表转为红黑树;结点少于8,又转为链表
总结:工业级散列表特性
- 支持快速增删查
- 内容占用合理
- 性能稳定,极端情况,性能也不会退化到无法接受
如何设计:
- 合适散列函数
- 合适装载因子阈值,设计动态扩容策略
- 合适的冲突检测
链表+散列表
LRU缓存淘汰算法
双向链表:prev和next维护
散列表拉链:hnext指针维护
查找:通过散列表和hnext拉链,找到数据,并将其移到双向链表尾部
删除:通过散列表找到数据,通过双向链表获取到前驱结点,删除,O(1)
添加:
已在缓存:将其移到双向链表尾部;
不在缓存:缓存未满,直接置于双向链表尾部;缓存已满,删除双向链表头结点,再将数据置于尾部。
Java LinkedHashMap
linked实际指双向链表
支持按照插入顺序遍历数据&按照访问顺序遍历数据
支持按照访问时间排序的时候,也是一个支持LRU缓存淘汰策略的缓存系统
哈希算法
- 从哈希值不能反向推导原始数据
- 输入数据敏感,雪崩效应
- 三裂冲突的概率小
- 执行效率高,较长文本也可以快速计算出长度一致的哈希值
加密/数字签名 MD5(消息摘要算法),SHA(安全散列算法)
唯一标识
图片:取图片二进制码串开头100个字节,中间100字节,最后100字节,通过哈希得到哈希字符串,作为唯一标识。
散列表中存储 唯一标识%图片路径
数据校验
P2P:多个机器并行下载2GB文件,文件被分割成许多文件块(假定为100块)。所有文件块下载完成,再组装成一个完整的文件。
将100个文件块分别取哈希值,存储在种子文件中。
散列函数
更加关注三散列后的值等否均匀分布,以及散列表的执行效率。
字典攻击
引入盐,与用户密码组合,增加密码复杂度。
哈希在分布式系统中的应用
负载均衡
如何实现一个会话粘滞的负载均衡算法:如何在同一个客户端上,将所有的请求都路由到同一个服务器上?
- 维护客户端IP/会话ID 与服务器编号的映射关系。
弊端:
客户端很多,映射表过大,浪费内存空间;
客户端上下线,服务器扩缩容,导致映射失败,映射表维护成本大
通过哈希,将客户端IP/会话ID哈嘻猴,与服务器列表大小进行取模运算,获得服务器编号。
数据分片
统计“搜索关键词”出现的次数
将数据分片,采用多台机器处理,提高处理速度。
n台机器并行,从日志中依次读取每个搜索关键词,计算哈希值,再与n取模,将数据分配到对应的机器上。每个机器再计算关键词出现的次数,合并就是最终的结果。
快速判断图片是否在图库中
n台机器,每台机器只维护某一部分图片对应的散列表。每次从图库中读取一个图片,计算唯一标识,与n个机器求余取模,取得要分配的机器编号。然后将图片的唯一标识和图片路径与机器编号构建散列表。
MD5计算标识:128bit,16Byte;文件长度上限256Byte,假设平均128Byte;链表法解决冲突存储指针,8byte—>每个数据单元152Byte
假设一台机器内存大小2GB,装载因子0.75,一台机器可以给1000w(2GB*0.75)张图片构建散列表,若对1e数据件索引,大约需要10台机器。
分布式存储
分片的思想,将数据存入不同的机器,若机器需要扩容时,所有的数据都要重新计算哈希值,并进行机器的搬移。会导致所有请求失效。
一致性哈希算法
- 分区间
假设有k个机器,数据的哈希值的范围是[0,MAX],所有的数据划分成m个小区间(m远大于k),每个机器负责m/k个小区间。有新机器加入的时候,只需要将某几个小区间的数据,从原来的数据搬移到新机器中。
不用全部重新哈希,搬移数据,也保证了各机器上数据数量均衡。 - 虚拟节点:解决哈希环偏移