普通的 Hash 解决的是什么问题?

下图是一个普通的余数法构造的哈希表。

一般在编程中使用哈希表,某个 bucket 突然就没了的概率比较小,常见的是因为负载因子太大需要增加 bucket,然后 rehash。

考虑在上图中,2 号 bucket 突然就没了会发生什么情形。

最直接的做法就是,后面的 bucket 依次往前面补位,然后重新计算 key 的 hash 值。

2 号 bucket 虽然只存放了一个 key,但是由于 2 号 bucket 的丢失,导致后面所有 bucket 存放的 key 的映射关系都失效了。

如果这个 hash 表在内存中,无非就是 rehash 一下,重新建立对应关系,问题不大。

但如果这种情况发生在分布式缓存中呢?这会导致大量的缓存失效,有可能导致很多请求直接冲到后端。

想想看,本来想用分布式缓存扛流量,结果仅仅因为其中一台缓存服务器挂掉了而导致整个缓存系统不可用,太脆弱了。

问题在于:每一个 key 映射到 bucket 的规则太精确了,完全没有余地,每个 bucket 挨得太密。对 key 计算完后的 hash 值直接指向了 bucket。

我是这么理解的,这种 Hash 表就好像多米诺骨牌,只要把其中的一块推倒,那么直接到后面的多米诺骨牌都倒了(对应的就是映射关系全失效了)。

一致性 Hash 是怎么解决这个问题的呢?

1997 年,麻省理工学院(MIT)的 David Karger 等 6 个人发布学术论文:

《Consistent hashing and random trees: distributed caching protocols for relieving hot spots on the World Wide Web》

中文翻译就是:一致性哈希和随机树:用于缓解万维网上热点的分布式缓存协议。

我想用我的理解来讲,接上面提到的看法是,这些 bucket 都挨得太紧密了,那这样得话,我何不就把每个 bucket 的距离搞得足够长呢?

原来 Hash 表的长度可能是 57,103,947,或者 2053 这种质数。

这次我玩一把大的,直接 Hash 表的长度是 2^32-1,然后把桶分布在这个上面,大概长这样:

我不太喜欢用环来描述,脑子不太好使,还是用最简单的数组描述把。

虽然这个一致性 Hash 表很长,但是真正用来装数据的 bucket 不多,bucket 通过 hash 算法(方法有很多)能均匀分布到这个很长的数组中。

一个 key 准备存取或者查找的过程是这样的:

怎么样,是不是很简单?不过事情还没完。

这样的好处就是,bucket 之间是隔离开的,增加或者减少 bucket 只会对局部有影响,不会影响到全局,具体自己去分析吧。

一致性 Hash 倾斜的问题

如果说实际的 bucket 本来就很少呢?这样会导致大量的键和某个 bucket 建立映射,key 分布不均匀。

这里的解决方法挺厉害的,就是虚拟 bucket,也就是我一个实际的 bucket 可以虚拟出很多个 bucket 来,那么这些虚拟的 bucket 只是名字和实际 bucket 不一样。

然后虚拟节点也会挂在 Hash 表中,类似这样的:

虚拟出来的节点越多,key 的分布越均匀。

总结

1. 隔离不同的 bucket,bucket 的增加或者减少只对局部有影响

2. 虚拟节点解决 key 分布不均匀

算法没有思想重要!

我想看了上面这些,应该可以自己写一个简单版的 Consistent Hash 了。下次我写个很 low 逼的给你们看看。

随机推荐

  1. Windows10易升下载

    为了更好的帮助用户快速跨版本升级windows,退出Windows易升!在线下载,更新安装!网速快的话需要半个小时搞定! 升级完毕,如股票感觉OK.记得清理C盘Windows.old文件 01.磁盘- ...

  2. asp.net mvc 如何将controller 里一个action 返回值为list<>的值输出到view

    在controller中:return View(myRole.ToList());在view文件开头加上:@model IEnumerable<LTXYCallCenter.Models.Ro ...

  3. 设置三思LED的IP地址跟端口号

    出厂设置是:202.11.11.01 初始端口号是:2929 设置虚拟机的ip跟LED的ip在一个网段上,在虚拟机上telnet命令,登陆到LED上面. 在/etc/init.d/rcS文件中, #! ...

  4. 初次使用百度地图API

    因为项目需要,不得不使用百度地图的API,以前从未了解过API,这不是唬人,真的,所以对百度地图API充满了恐惧,但是到后面,已经麻木了.期间遇到过很多错误,每一个都弄得头大,借博客的名义把平时遇到的 ...

  5. log4j 实例 , 浅析

    一.新建log4j.propperties,放在工程的src目录下. #fileAppender log4j.rootCategory = DEBUG,file,consoleAppender log ...

  6. uGUI使用代码动态添加Button.OnClick()事件(Unity3D开发之十二)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/42705885 ...

  7. Kali学习笔记4:Wireshark详细使用方法

    Kali Linux自带Wireshark工具使用介绍: 1.进入界面 这里Lua脚本报错,无需关注 开始使用: 双击第一个eth0:以太网0,开始抓包: 点击上边的这个按钮可以设置: 这里注意:需要 ...

  8. BZOJ_1085_[SCOI2005]骑士精神_IDDFS

    BZOJ_1085_[SCOI2005]骑士精神_DFS Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑 士的走法(它可 ...

  9. JetBrain系列IDE提示Filesystem Case-Sensitivity Mismatch的解决

    目录 解决方法 1. 用文本编辑器修改APP包文件中的属性文件(不推荐) 2. 复制或新建属性文件到APP的启动目录,添加对应的属性项(推荐) 解决方法1 1. 用文本编辑器修改APP包文件中的属性文 ...

  10. 踩坑之路---JWT验证

    使用JWT验证客户的携带的token 客户端在请求接口时,需要在request的head中携带一个token令牌 服务器拿到这个token解析获取用户资源,这里的资源是非重要的用户信息 目前我的理解, ...