一,什么是哈希算法
哈希和散列其实意思是一样的,只是中文翻译的区别,英文是 Hash
。
哈希算法也叫 hash
算法或散列算法。哈希算法的定义:将任意长度的二进制串映射为固定长度(一般是 128 bit
)的二进制串,这个映射的规则就是哈希算法。而通过原始数据映射之后得到的二进制值串就是哈希值。
一个优秀的哈希算法一般需要满足以下几点要求(来源:《数据结构和算法之美》作者-王争的经验):
- 从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法);
- 对输入数据非常敏感,哪怕原始数据只修改了一个 Bit,最后得到的哈希值也大不相同;
- 散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小;
- 哈希算法的执行效率要尽量高效,针对较长的文本,也能快速地计算出哈希值。
hash(key)
中的key
可以是字符串、数字、对象等,但其底层都是二进制串。
对应的哈希算法的特点如下:
- 不可逆,即通过密文无法反推生成明文。
- 原始字符串不一样,得到的哈希结果不一样。
- 冲突的概率要很小。
- 执行效率要高,理论上来说执行时间越长的冲突概率越小。
二,哈希算法的应用场景
1,安全加密。
最常用于加密的哈希算法是 MD5
(MD5 Message-Digest Algorithm,MD5 消息摘要算法)和 SHA
(Secure Hash Algorithm,安全散列算法)。
对用于加密的哈希算法来说,有两点格外重要。第一点是很难根据哈希值反向推导出原始数据,第二点是散列冲突的概率要很小。
对于第二点要求,实际上,不管是什么哈希算法,我们只能尽量减少碰撞冲突的概率,理论上是没办法做到完全不冲突的。为什么这么说呢?依据是基于组合数学中一个非常基础的理论:鸽巢原理(也叫抽屉原理)。鸽巢原理: 槽是固定的,数据是无限的(不定的),冲突是有概率存在的。
2,唯一标识。
用在图片上的情况比较多,如果同一张图片上传多次,会通过哈希算法对图片的二进制内容进行计算,以计算结果为标识一张图片是否已经上传过。如果一张图片过大,可以采用部分二进制,比如开头 100K
,中间 100K
,最后 100K
相加进行哈希算法来保证一定的执行效率。
3,文件完整性校验。
比如迅雷等 p2p
下载器,从不同主机下载文件分片最终在本地合成一个文件。种子文件里面一般存储了各个文件分片的哈希结果,下载完成后通过本地计算各个分片的哈希再跟种子里面存储的核实来确保文件分片没有被恶意更改过。
4,散列函数。
在散列表中需要的哈希算法,一般对执行效率要求高,对是否冲突要求比较低,因为散列表通常都会有冲突的解决方式,比如开放寻址法跟链表法。
5,负载均衡。
何谓一个会话粘滞(session sticky)的负载均衡算法?即在同一个客户端上,在一次会话中的所有请求都路由到同一个服务器上。
通过哈希算法,对客户端 IP
地址或者会话 ID
计算哈希值,将取得的哈希值与服务器列表的大小进行取模运算,最终得到的值就是应该被路由到的服务器编号。 这样,我们就可以把同一个 IP
过来的所有请求,都路由到同一个后端服务器上。
6,数据分片。
哈希算法用于数据分片的一个例子是统计“搜索关键字”出现的次数。
假如有一个 1T
的日志文件,这里面记录了用户的搜索关键词,如果想要快速统计出每个关键词被搜索的次数,解决方案是什么?
分析问题:1,1T
的日志文件很大,内存中基本不可能放下这么多数据;2,数据很多,1
台机器处理速度会很慢。
解题思路:参考 Map-Reduce
原理。先对数据进行切片,然后使用多台机器并行处理。
解题方案:使用 n
台机器进行数据处理,首先从日志文件中顺序依次读取每个关键字,并用哈希函数计算得到哈希值,并跟 n
取模,取模得到的值就是对应的机器编号,这样哈希值相同的关键字就被分配到同一台机器上进行处理。
最后每台机器分配统计关键字出现的次数,然后合并在一起就是最终的结果。
从上可以看出,针对这种海量数据的处理问题,我们可以采用多机分布式处理。借助这种分片的思路,可以突破单机内存、CPU 等资源的限制。
7,分布式存储。
负载均衡、数据分片、分布式存储,这三个应用都跟分布式系统有关,也就是说哈希算法是可以解决这些分布式系统问题。
现在互联网面对的都是海量的数据、海量的用户。我们为了提高数据的读取、写入能力,一般都采用分布式的方式来存储数据,比如分布式缓存。我们有海量的数据需要缓存,所以一个缓存机器肯定是不够的。于是,我们就需要将数据分布在多台机器上。
该如何决定将哪个数据放到哪个机器上呢?我们可以借用前面数据分片的思想,即通过哈希算法对数据取哈希值,然后对机器个数取模,这个最终值就是应该存储的缓存机器编号。
但是这会产生一个问题,就是当数据增多的情况下,机器要扩容,增加机器数量,原来的 n
假设是 10
,现在变成了 20
,这样按照之前的方法所有的数据都要重新计算哈希值,然后重新搬移到正确的机器上。这样就相当于,缓存中的数据一下子就都失效了。所有的数据请求都会穿透缓存,直接去请求数据库。这样就可能发生雪崩效应,压垮数据库。
所以,我们需要一种方法,使得在新加入一个机器后,并不需要做大量的数据搬移。
这就是一致性哈希算法。假设我们有 k k k 个机器,数据的哈希值的范围是 [ 0 , M A X ] [0, MAX] [0,MAX],我们将整个范围划分成 m m m 个小区间( m m m 远大于 k k k),那么每个机器就负责 m / k m/k m/k 个小区间。当有新机器加入的时候,我们就将某几个小区间的数据,从原来的机器中搬移到新的机器中。这样,既不用全部重新哈希、搬移数据,也保持了各个机器上数据数量的均衡。
总结
在负载均衡应用中,利用哈希算法替代映射表,可以实现一个会话粘滞的负载均衡策略。在数据分片应用中,通过哈希算法对处理的海量数据进行分片,多机分布式处理,可以突破单机资源的限制。在分布式存储应用中,利用一致性哈希算法,可以解决缓存等分布式系统的扩容、缩容导致数据大量搬移的难题。