例如数字与字母的结合,输出的就为“哈希值”。从数学术语上说,就是这个哈希函数,是将任意长度的数据,映射在有限长度的域上。总体而言,哈希函数用于,将消息或数据压缩,生成数据摘要,最终使数据量变小,并拥有固定格式。
那么哈希算法的作用又是什么呢?
(1) 在庞大的数据库中,由于哈希值更为短小,被找到更为容易,因此,哈希使数据的存储与查询速度更快。
(2) 哈希能对信息进行加密处理,使得数据传播更为安全。
哈希算法解决了什么生活问题?
看似深奥的数学函数,又或是计算机程序的哈希算法,其实跟我们的生活息息相关。就拿每年双十一的快递来说,实际上,哈希算法原理提高了快递入库出库的速度。
引言
将任意长度的二进制字符串映射为定长二进制字符串的映射规则我们称为散列(hash)算法,又叫哈希(hash)算法,而通过原始数据映射之后得到的二进制值称为哈希值。哈希表(hash表)结构是哈希算法的一种应用,也叫散列表。用的是数组支持按照下标随机访问数据的特性扩展、演化而来。可以说没有数组就没有散列表。
哈希算法主要特点
从哈希值不能反向推导原始数据,也叫单向哈希。
对输入数据敏感,哪怕只改了一个Bit,最后得到的哈希值也大不相同。
散列冲突的概率要小。
哈希算法执行效率要高,散列结果要尽量均衡。
哈希算法的核心应用
安全加密 :对于敏感数据比如密码字段进行MD5或SHA加密传输。
唯一标识 :比如图片识别,可针对图像二进制流进行摘要后MD5,得到的哈希值作为图片唯一标识。
散列函数 :是构造散列表的关键。它直接决定了散列冲突的概率和散列表的性质。不过相对哈希算法的其他方面应用,散列函数对散列冲突要求较低,出现冲突时可以通过开放寻址法或链表法解决冲突。对散列值是否能够反向解密要求也不高。反而更加关注的是散列的均匀性,即是否散列值均匀落入槽中以及散列函数执行的快慢也会影响散列表性能。所以散列函数一般比较简单,追求均匀和高效。
*负载均衡 :常用的负载均衡算法有很多,比如轮询、随机、加权轮询。如何实现一个会话粘滞的负载均衡算法呢?可以通过哈希算法,对客户端IP地址或会话SessionID计算哈希值,将取得的哈希值与服务器列表大小进行取模运算,最终得到应该被路由到的服务器编号。这样就可以把同一IP的客户端请求发到同一个后端服务器上。
*数据分片 :比如统计1T的日志文件中“搜索关键词”出现次数该如何解决?我们可以先对日志进行分片,然后采用多机处理,来提高处理速度。从搜索的日志中依次读取搜索关键词,并通过哈希函数计算哈希值,然后再跟n(机器数)取模,最终得到的值就是应该被分到的机器编号。这样相同哈希值的关键词就被分到同一台机器进行处理。每台机器分别计算关键词出现的次数,再进行合并就是最终结果。这也是MapReduce的基本思想。再比如图片识别应用中给每个图片的摘要信息取唯一标识然后构建散列表,如果图库中有大量图片,单机的hash表会过大,超过单机内存容量。这时也可以使用分片思想,准备n台机器,每台机器负责散列表的一部分数据。每次从图库取一个图片,计算唯一标识,然后与机器个数n求余取模,得到的值就是被分配到的机器编号,然后将这个唯一标识和图片路径发往对应机器构建散列表。当进行图片查找时,使用相同的哈希函数对图片摘要信息取唯一标识并对n求余取模操作后,得到的值k,就是当前图片所存储的机器编号,在该机器的散列表中查找该图片即可。实际上海量数据的处理问题,都可以借助这种数据分片思想,突破单机内存、CPU等资源限制。
*分布式存储 :一致性哈希算法解决缓存等分布式系统的扩容、缩容导致大量数据搬移难题。
JDK集合工具实现 :HashMap、 LinkedHashMap、ConcurrentHashMap、TreeMap等。Map实现类源码分析,详见 https://www.jianshu.com/p/602324fa59ac
总结
本文从哈希算法的原理及特点,总结了哈希算法的常见应用场景。
其中基于余数思想和同余定理实现的哈希算法(除留取余法),广泛应用在分布式场景中(散列函数、数据分片、负载均衡)。由于组合数学中的“鸽巢”原理,理论上不存在完全没有冲突的哈希算法。(PS:“鸽巢”原理是指有限的槽位,放多于槽位数的鸽子时,势必有不同的鸽子落在同一槽内,即冲突发生。同余定理:如果a和b对x取余数操作时a%x = b%x,则a和b同余)
构造哈希函数的常规方法有:数据分析法、直接寻址法、除留取余法、折叠法、随机法、平方取中法等 。
常规的解决哈希冲突方法有开放寻址法(线性探测、再哈希)和链表法。JDK中的HashMap和LinkedHashMap均是采用链表法解决哈希冲突的。链表法适合大数据量的哈希冲突解决,可以使用动态数据结构(比如:跳表、红黑树等)代替链表,防止链表时间复杂度过度退化导致性能下降;反之开放寻址法适合少量数据的哈希冲突解决。
通俗来讲,哈希值就是文件的身份证,不过比身份证还严格。他是根据文件大小,时间,类型,创作者,机器等计算出来的,很容易就会发生变化,谁也不能预料下一个号码是多少,也没有更改他的软件。哈希算法将任意长度的二进制值映射为固定长度的较小二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的输入,在计算上是不可能的。
有这样一种情境,有三万张图片我们要均匀放置于三个缓存服务器上
简单的做法是对缓存的key进行哈希计算,得到的值进行取模计算,所得到的余数,便是缓存的服务器编号
hash % 机器数 = 余数
当机器数为3时无论值为多少,其余数永远只有0,1,2三种情况
那么根据余数,我们给服务器进行编号s0,s1,s2,余数为0的放置于s0服务器上,1,2同理。
这样我们就将三万张图片的缓存均分成三份存放与三台缓存服务器中
因为对同一张图片进行哈希计算时,所得到的哈希值是不变的,所以当需要访问图片时,只要再次进行哈希计算和取模计算,就能获取到图片存放于哪台服务器,便可以去该服务器中查找满足了我们的需求。而这种算法也称之为哈希算法
这其中有一个问题,那便是如果我增加一台服务器呢
可以预见的是,当增加一台服务器服务器数变成了4.而余数也出现了4种情况
这时向s2的服务器查询时,无法读取到图片,这导致了程序无法从缓存服务器中读取数据,这时程序就会向后端服务器请求,而大量的缓存同时失效,会导致所有请求都指向后端服务器,这会引起后端服务器的崩溃。
这是就要引入一致性哈希算法
还是同样的三个缓存服务器,这次我们将哈希值对2 32取模,所得到的数一定是1到2 32之间的一个整数
然后我们想像一个圆环,其上的每一个点都代表1到2^32之间的一个整数,而这个圆环也被称为hash环
之后我们对服务器A进行取模计算,这样算出来的整数肯定在1到2^32之间,将这个整数代表为服务器A,并且我们可以将这个整数映射到哈希环上,同样的道理我们处理另外两个服务器,这时三个服务器都被映射到了哈希环上,对于图片我们也将他映射到哈希环上
那么我们只要从图片的哈希值开始,沿顺时针在哈希环上查找,遇到的第一个服务器便是图片缓存所在的服务器
这时哪怕新添加一个服务器在哈希环上,我门所丢失的缓存数据也只是新添加的服务器到逆时针方向遇到的第一个服务器这部分数据,而这样仍然有大部分缓存在缓存服务器中可以被查找到,这样可以帮助后端服务器分担大部分压力,不会使服务器崩溃,而这部分丢失的缓存数据,之后重新在后端加载便可以了
这又引入了另一个问题,哈希偏斜
我们无法确保三个服务器在哈希环上为均分的状态,很有可能其中一台服务器分到了很大部分而另两台分到了很少的部分,这样同样会有后端服务器崩溃的隐患
我们可以添加很多虚拟结点同一个服务器我们分出许多虚拟节点,映射在哈希环上,哈希环上的节点越多,缓存被均分的概率便越大,这样可以尽可能的保证缓存在服务器上是接近理想均分的状态,避免了哈希偏斜的问题
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)