9.哈希算法在分布式系统中原来这么有用
哈希算法:将任意长的二进制值串映射为固定长的二进制值串
哈希算法需满足以下几点
-
从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法)
-
对输入数据非常敏感,哪怕原始数据只修改了一个 Bit,最后得到的哈希值也大不相同
-
散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小
-
哈希算法的执行效率要尽量高效,针对较长的文本,也能快速地计算出哈希值
哈希算法应用
-
安全加密
-
要求1:很难通过哈希值反推原始数据
-
要求2:散列冲突概率要小(实际上每个哈希函数都要尽量做到这一点)
-
鸽巢原理说明哈希算法无法做到0冲突
-
-
唯一标识
- 场景:图库中是否存在某张图片
-
先给每张图片创建一个唯一标识,比如可以从图片的开头、中间、末尾各取100字节放到一起,通过哈希算法得到一个唯一的哈希字符串作为图片的唯一标识
-
将文件的唯一标识和存储地址都存储在散列表中(并不是映射表)
-
查找时先通过散列函数找到唯一标识,再在散列表中查找是否存在这个唯一标识,若未找到,说明图库中不存在此图片;否则通过散列表中存储的文件路径做全量对比(防止散列冲突)
-
- 场景:图库中是否存在某张图片
-
数据校验
-
BT下载基于P2P协议,文件可能被分为很多快,存储在多台机器上
-
下载完成后对每一个文件块分别求哈希值,看是否与BT文件中存储的源文件的哈希值相同,若不同则重新从其他机器上下载这个文件块
-
-
散列函数
-
对散列冲突的要求比较低
-
追求简单高效,散列值是否均匀分布
-
-
负载均衡
- 场景:要将同一个客户端在一次会话中的所有请求都路由到同一个路由器上(会话粘滞的负载均衡算法)
-
对客户端IP进行哈希运算
-
取得的哈希值与服务器列表的大小取模
-
得到的值就是被路由到的服务器编号
-
- 场景:要将同一个客户端在一次会话中的所有请求都路由到同一个路由器上(会话粘滞的负载均衡算法)
-
数据分片
-
场景1.统计搜索关键词出现次数:现在有1T的日志文件,记录了用户的搜索关键词,现在想要快速统计出每个关键词被搜索的次数
-
数据量很大,无法放到单个机器内存中,并且只用一台机器处理,花费时间将会很长,所以采用多台机器处理的方法
-
从日志文件中依次读取搜索关键字,通过哈希值取模分配机器编号(也就是相同的关键词会分配到相同的机器)
-
每个机器分别统计各个关键词次数
-
合并
-
-
场景2.快速判断图片是否在图库中
-
和上面提到的不同,假设现在有一亿张图片,那么在单台机器上构建散列表是行不通的,所以同样对数据进行分片,多机处理
-
准备 n 台机器,让每台机器只维护某一部分图片对应的散列表
-
每次从图库中读取一个图片,计算唯一标识,然后与机器个数 n 求余取模,得到的值就对应要分配的机器编号,然后将这个图片的唯一标识和图片路径发往对应的机器构建散列表
-
要判断一个图片是否在图库中的时候,我们通过同样的哈希算法,计算这个图片的唯一标识,然后与机器个数 n 求余取模。假设得到的值是 k,那就去编号 k 的机器构建的散列表中查找
-
-
-
分布式存储
-
面对海量的数据,为了提高数据的读取、写入能力,一般都采用分布式的方式来存储数据,比如分布式缓存
-
借助前面数据分片的思想,对数据进行哈希,对哈希值取模分配机器
-
当数据增多,原来的机器已经无法容纳,需要扩容时问题就来了
-
机器列表长度改变,导致原来缓存的哈希值与现在的不一样,所有的数据都要重新计算哈希值,搬移到新的机器上
-
也就是说缓存全部失效,所有的请求都会穿透缓存,直接去请求数据库,这就可能发生雪崩效应,压垮数据库
-
解决:一致性哈希算法
-
基本思想
-
假设我们有 k 个机器,数据的哈希值的范围是[0, MAX]
-
我们将整个范围划分成 m 个小区间(m 远大于 k),每个机器负责 m/k 个小区间
-
当有新机器加入的时候,我们就将某几个小区间的数据,从原来的机器中搬移到新的机器中
-
这样,既不用全部重新哈希、搬移数据,也保持了各个机器上数据数量的均衡
-
-
实现上借助一个虚拟的环和虚拟节点
- 参考文章http://www.zsythink.net/archives/1182
-
-