1、什么是哈希?
哈希也叫散列,因而哈希表也叫散列表。通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数。采用散列函数将记录存储在一块连续的存储空间中,这块连续的存储空间称为哈希表。所得的存储地址称为哈希地址。
2、哈希的常见构造方法
2.1、直接定址
取关键字的某个线性函数称为散列地址。即H(key)=key或者H(key)=a*key+b(a,b为常数)。假设有数组{‘A’,‘B’,'C','D'、‘F’、‘D’、‘E’、‘F’、‘C’},求该字符数组每个字符的出现次数。利用ASCII码,哈希函数可以通过直接定址法H(key)=key-'A'(ASCII码65),这样每一个key都可以将它的H(key)值,将其当成数组下标放在一个长度为26的int数组中。array[0]表示字母A出现的次数,array[1]表示字母B出现的次数。
2.2、数字分析
处理关键字位数比较多、数字出现频率不高的情况,通过抽取关键字的一部分进行操作,计算哈希存储位置。
假设在记录员工的生日,如20000618.若直接把年月日作为哈希地址,空间浪费很大,若是将月日作为哈希地址,既能减少空间浪费又能降低哈希冲突的可能性。
2.3、平方取中
当无法确定关键字中哪几位分布比较均匀时,可以求关键字的平方值,然后取平方值的中间几位作为散列地址。
2.4、除留取余
最常用的构造哈希函数方法。哈希函数为:H(key)=key mod p
其中mod为取余运算,p<m,m为哈希表表长。此方法不仅可以对关键字直接取模,也可以在折叠、平方取中之后再取模。
2.5、折叠法
适用于关键字位数很多,而且关键字每一位上数字分布大致均匀的情况。将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后这几部分的叠加和舍去进位后作为散列地址。假设关键字1472580369,表长为3。可将关键字分成4部分,147|258|036|9,147+258+036+9=450,取450为散列地址。
2.6、随机数
选择一个随机数,取关键字的随机函数值作为它的哈希地址。用于关键字长度不同的集合。哈希函数为:H(key)=random{key) 其中random是随机函数。
2.7、如何选择
(1)计算哈希地址所需时间
(2)关键字长度
(3)哈希表大小
(4)关键字分布情况
(5)查找频率
3、哈希冲突
不同数据的关键字通过哈希函数得到一个相同的地址,这时就发生冲突,这种冲突一般叫做哈希冲突。把冲突的关键字称为这个哈希函数的同义词。
解决办法常见的有4种:开放地址法、再哈希法、链地址法、公共区溢出法。
(1)开放地址法-线性探测
一旦发生冲突,就寻找下一个空的哈希地址,只要哈希表足够大,总能找到地址存放。
(2)开放地址法-二次探测
在线性探测的基础上,多了寻找上一个空的哈希地址的方法。
(3)开放地址法-随机探测
随机寻找一个空的哈希地址。
(4)再哈希法
遇到哈希冲突时,换一个哈希函数计算。
(5)链地址法
将同义词存储在一个单链表中,我们称这种单链表为同义词子表,哈希表中存储同义词子表的头指针。下图中的哈希函数为:H(key)=key mod 13。可以看出1、14、27、79取余结果都是1,哈希表中的地址1保存了1、14、27、79这个链表的头指针。
4、哈希函数的使用场景
(1)负载均衡
根据后端节点的某个固定属性(如来源ip、cookie、用户名等)计算哈希值,然后把所有节点计算出来的哈希值组成一个圆环。请求过来时根据请求的特征计算该特征的哈希值,然后顺时针查找哈希环上的哈希值,第一个比请求特征的哈希值大的哈希值所对应的节点即为被选中的节点。
(2)加密算法
常见的加密算法都是哈希函数的实现:MD5、SHA、DES、AES。
(3)唯一标识
任何文件在计算中都可以表示成二进制串。我们若是想要比对两个文件相等,可以给每个图片去一个唯一标识(信息摘要)。通过哈希函数得到一个哈希值,用它作为图片的唯一标识。用唯一标识来判定图片是否在图库中。
(4)分布式存储
为提高读、写能力,一般采用分布式存储,比如分布式缓存。因为大量数据要缓存,所以需要将数据分布在多机。对机器个数n取模,就得到该缓存数据应存储的机器编号。
若是m个机器无法支撑,扩容到了m+n个机器,原来的哈希值都失效,需要重新计算,这将会导致穿透缓存直接请求数据库。
这时候可以使用一致性哈希函数。
假设我们又k个机器,数据的哈希值的范围是[0,MAX];将[0,MAX]划分为m个小区间(m远小于k),每个机器负责m/k哥小区间。当新机器加入的时候,我们就将某个小区间的数据从原来的机器中搬移到新的机器中。这样,既不用全部重新哈希,也保持了各个机器上的数据数量均衡。
一致性哈希算法将整个哈希值空间组织成一个虚拟的圆环,假设哈希函数的值空间为0到2^32 -1。从0到2^32 -1代表的分别是一个个的节点,这个环也叫哈希环。我们将我们的节点进行一次哈希,按照一定的规则,让节点落在哈希环上。
我们约定,通过数据key的哈希值落在哈希环上的节点,如果命中了机器节点就落在这个机器上,否则落在顺时针直到碰到第一个机器。A的哈希值落在了D2节点的前面,落在了D2机器上,D的哈希值落在D1节点的前面,落在了D1机器上,B的哈希值刚好落在了D1机器上,其他依此类推。
当机器减少或增加的时候,假设D2因为一些原因宕机了,只有数据A的记录需要重新定位存储到节点D1上,其他的数据都没有被影响到;当我们需要增加机器D4,在D2和D1之间时,之前落在D2到D4之间的数据都需要重新定位到D4上面。