参考 https://www.sohu.com/a/411095977_115128#
一致性哈希
概述
- 基础的哈希表的东西就不多说了。
- 在分布式缓存集群中,如何做缓存负载均衡?在分布式集群缓存的负载均衡实现中,比如 memcached 缓存集群,需要把缓存数据的 key 利用哈希函数散列,这样缓存数据能够均匀分布到各个分布式存储节点上,要实现这样的负载均衡一般可以用哈希算法来实现。
普通哈希算法做负载均衡的弊端:
-
假如用一个简单的「取模法」来说明这个过程。
-
假设有 3 个服务器节点编号 [0 - 2],6 个缓存键值对编号 [1 - 6],则完成哈希映射之后,三个缓存数据映射情况如下:
-
哈希计算公式:key % 节点总数 = Hash节点下标,所以分布如图:
-
这时会有两个问题:扩容 和 容错。
- 首先扩容上来说,假如我需要加入一个机器,那么这个哈希函数就得变化,比如现在有4个机器了,那么取模就得取4。这样基本上要把机器中的缓存重新迁移,变动比较大。
- 再者容错性较差,假如现在某个机器宕机了,机器数量变少,哈希函数还得变化,负载还得迁移。
-
一致性哈希
一致哈希由 MIT 的 David Karger 及其合作者提出,现在这一思想已经扩展到其它领域。在这篇1997年发表的学术论文中介绍了一致哈希如何应用于用户易变的分布式Web服务中。一致哈希也可用于实现健壮缓存来减少大型Web应用中系统部分失效带来的负面影响。
-
一句话概括一致性哈希:就是普通取模哈希算法的改良版,哈希函数计算方法不变,只不过是通过构建环状的 Hash 空间代替普通的线性 Hash 空间。具体做法如下:
-
首先,选择一个足够大的Hash空间(一般是 0 ~ 2^32)构成一个哈希环。
-
然后,对于缓存集群内的每个存储服务器节点计算 Hash 值,可以用服务器的 IP 或 主机名计算得到哈希值,计算得到的哈希值就是服务节点在 Hash 环上的位置。
-
最后,对每个需要存储的数据 key 同样也计算一次哈希值,计算之后的哈希也映射到环上,数据存储的位置是沿顺时针的方向找到的环上的第一个节点。
-
-
一致性哈希做负载均衡的扩容:
- 当缓存服务集群要新增一个节点node3时,受影响的只有 key3 对应的数据 value3(即从这个新节点~上一个节点之间的数据,从node0移动过来即可),其余节点存储的数据保持不动。
- 当缓存服务集群要新增一个节点node3时,受影响的只有 key3 对应的数据 value3(即从这个新节点~上一个节点之间的数据,从node0移动过来即可),其余节点存储的数据保持不动。
-
一致性哈希做负载均衡的容错性:
- 假设 node2 节点宕机下线,则原来存储于 node2 的数据 value2 和 value5 ,只需按顺时针方向选择新的存储节点 node0 存放即可,不会对其他节点数据产生影响。一致性哈希能把节点宕机造成的影响控制在顺时针相邻节点之间,避免对整个集群造成影响。
- 假设 node2 节点宕机下线,则原来存储于 node2 的数据 value2 和 value5 ,只需按顺时针方向选择新的存储节点 node0 存放即可,不会对其他节点数据产生影响。一致性哈希能把节点宕机造成的影响控制在顺时针相邻节点之间,避免对整个集群造成影响。
-
一致性哈希的问题:
- 数据倾斜:有可能节点挤到一起了,负载不均匀,单个节点压力很大。
- 节点雪崩:比如node2宕机了,那么node2的请求都去了node0,node0再崩了,大量数据请求又到node1,这样就会雪崩。一连串连锁反应使得缓存不可用。
-
问题的解决:
- 虚拟节点:一个物理节点的机器可以映射到多个虚拟节点。
- 多个机器的多个虚拟节点可以均匀遍布哈希环,解决数据倾斜的问题。
- 再者如果某个节点崩了,那么他的多个虚拟节点取消了之后,数据均匀分摊到其他的节点(只要他的每一个虚拟节点后面的那个节点不都是同一个机器),那么则不会造成雪崩的情况。
- 虚拟节点:一个物理节点的机器可以映射到多个虚拟节点。