【算法】理解哈希算法 hash 和常见应用

概念

将任意长度的二进制值串映射为固定长度的二进制值串,这个映射的规则就是 哈希算法
通过原始数据映射之后得到的二进制值串就是 哈希值

要求

  • 从哈希值不能反向推导出原始数据
  • 对输入数据非常敏感,一个 Bit 修改得到的哈希值也大不相同
  • 散列冲突的概率要很小
  • 执行效率高效

常见应用

安全加密、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。

后三个应用均与分布式系统有关。

下面逐个进行解释。

1 安全加密

常用:

  • MD5 (MD5 Message-Digest Algorithm , MD5 消息摘要算法)
  • SHA (Secure HashAlgorithm ,安全散列算法)

其他:

  • DES ( Data Encryption Standard ,数据加密标准)
  • AES ( Advanced Encryption Standard ,高级加密标准)

哈希算法在安全加密方面有两点特别重要:

  • 很难反向推到
  • 散列冲突概率小

以MD5为例,哈希值是固定的128 位二进制串,最多能表示 2^128 个数据。当我们对2^128+1个数据求哈希值时,必然会存在至少两个哈希值是相同的。

尽管如此,想要破解的难度依然非常高。

2 唯一标识

对大数据做信息摘要,通过一个较短的二进制编码来表示很大的数据。

查找图库中是否存在某张图片 为例。

从图片 开头、中间、末尾各取 100 个字节,将这 300 个字节放到一块,通过哈希算法(比如 MD5 ),得到一个哈希字符串,用它作为图片的唯一标识。

通过唯一标识来判定图片是否在图库中,这样就可以减少很多工作量。

还可以把图片路径信息,存储在散列表中。
如果存在,通过散列表中存储的文件路径,获取到这个已经存在的图片。
跟现在要插入的图片做全量的比对,看是否完全一样。
如果一样,就说明已经存在;
如果不一样,说明两张图片尽管唯一标识相同,但是并不是相同的图片。

3 数据校验

校验数据的完整性和正确性。

BT下载时,文件可能分成很多文件块,并行的下载完成后需要将文件块组装。
组装时需要校验文件块是否完整或是否正确,避免下载出错或被恶意篡改。

一种校验思路:
对所有文件块取哈希值,保存在种子中。
下载完成后,逐一对文件块的哈希值进行比对。
如果不同,则需要重新从其他宿主机器上下载这个文件块。

4 散列函数

散列函数其实也是哈希算法的一种应用。
散列函数是设计一个散列表的关键。
散列函数中用到的散列算法,更加关注散列后的值是否能平均分布,以及执行效率。

插个问题:如何防止数据库中的用户信息被脱库

对用户密码进行加密之后再存储,比如 SHA 等(因为 MD5 已经号称被破解了)。

但由于很多人使用简单密码,为了更加安全,需要维护一个常用密码的字典表。
把字典中的每个密码用哈希算法计算哈希值,然后拿哈希值跟脱库后的密文比对。
如果相同,基本上就可以认为,这个加密之后的密码对应的明文就是字典中的这个密码。
(但由于散列冲突,也有可能出现,尽管密文一样,但是明文并不一样的情况。)

引入一个盐( salt ),跟用户的密码组合在一起,增加密码的复杂度。

5 负载均衡

在同一个客户端上,在一次会话中的所有请求都路由到同一个服务器上。

对客户端IP 地址或者会话 ID 计算哈希值,将取得的哈希值与服务器列表的大小进行取模运算,最终得到的值就是应该被路由到的服务器编号。

这样,我们就可以把同一个 IP 过来的所有请求,都路由到同一个后端服务器上。

6 数据分片

两个例子来说明。

统计 “ 搜索关键词 ” 出现的次数

假如有 1T 日志文件,记录了用户的搜索关键词,想要快速统计出每个关键词被搜索的次数。

可以先对数据进行分片,然后采用多台机器处理来提高速度。

依次读出每个关键词,通过哈希函数计算哈希值,然后跟 n取模,得到的值就是应该被分配到的机器编号。

哈希值相同的搜索关键词就被分配到了同一个机器上。每个机器会分别计算关键词出现的次数,最后合并起来就是最终的结果。

这里的处理过程也是 MapReduce 的基本设计思想。

快速判断图片是否在图库中

上面唯一标识中讲过,给每个图片取唯一标识(或者信息摘要),然后构建散列表。

但图库中的图片数量非常大(上亿)时,内存有限,单台机器构建散列表行不通。

可以对数据进行分片,然后采用多机处理。每台机器只维护部分图片对应的散列表
每次从图库中读取一个图片,计算唯一标识,与机器个数 n 求余取模,得到的值就对应要分配的机器编号,然后将这个图片的唯一标识和图片路径发往对应的机器构建散列表。

判断一个图片是否在图库中时,通过相同哈希算法,计算这个图片的唯一标识,然后与机器个数 n 求余取模。得到的值对应机器编号,将这个图片的唯一标识和图片路径发往对应的机器构建散列表。

下面估算一亿张图片构建散列表所需大概的机器数。

假设通过 MD5 来计算哈希值,长度128比特,也就是 16 字节。
文件路径长度上限是 256 字节,假设平均长度是 128 字节。
如果用链表法来解决冲突,还需要存储指针,指针占用 8 字节。
所以散列表中每个数据单元就占用 152 字节(估算)。
假设每台机器的内存2GB ,散列表的装载因子为 0.75 ,那一台机器可以给大约 1000万( 2GB*0.75/152 )张图片构建散列表。
所以对 1 亿张图片构建索引,需要大约十几台机器。

合理的估算能更好地评估解决方案的可行性。

7 分布式存储

面对海量数据,为了提高数据的读取、写入能力,一般都采用分布式的方式来存储数据,比如分布式缓存。

借用前面数据分片的思想,即通过哈希算法对数据取哈希值,然后对机器个数取模,这个最终值就是应该存储的缓存机器编号。

但是这样存在一个问题,假如后期需要新增或下线机器,个数发生改变,会造成缓存数据全部失效。

这时,需要一致性哈希算法,具体可以查看 聊聊一致性哈希 、漫画算法:什么是一致性哈希?

大概含义是:

实际k台机器,但划分时使用m作为个数(m远大于k),取模时,使用m。
当被分配到不存在的机器上时,自动划分到下一个存在的机器中。
当新增机器时,只需调整新增机器与新增机器下一个存在的机器之间的缓存数据即可。
下线机器时,只需调整该机器与下一台机器之间的数据即可,实际中为了避免突然机器下线,往往会在每台机器中同步上一台机器数据。
为了解决最后一台机器的数据问题,往往还会把哈希空间做成环状结构。

参考:

  1. 极客时间 - 数据结构与算法之美 ;
  2. 聊聊一致性哈希 ;

你可能感兴趣的:(数据结构与算法)