产生的原因
- 在解决分布式系统中负载均衡的问题的时候可以使用Hash算法让固定的一部分请求落到同一台服务器上, 这样每台服务器固定处理一部分请求(并维护这些请求的信息), 起到负载均衡的作用。
- 但是普通的余数hash算法伸缩性很差, 当新增或者下线服务器机器的时候, 用户id与服务器的映射关系会大量失效。一致性hash则利用hash环对其进行了改进。
- 改进减少了大量失效的可能性, 并没有根本解决映射关系失效的问题。
概述
-
假设有五台服务器, 地址分别为node1, node2, node3, node4, node5。
-
一致性hash首先计算四个ip地址对应的hash值: hash(node1), hash(node2), hash(node3), hash(node4), 计算出来的hash值是0~最大正整数之间的一个值。
-
hash环上顺时针从整数0开始, 一直到最大整数, 我们根据五个ip计算的hash值肯定会落到这个hash环上的某一个点, 至此我们把服务器的四个ip映射到了一致性hash环
-
当用户在客户端进行请求的时候, 首先根据hash(userId)计算路由规则(hash值), 然后看hash值落到了hash环的哪个地方, 根据hash值在hash环上的位置顺时针找距离最近的ip作为路由ip
-
如果有服务器挂了, 那么该挂掉的服务器上的请求会被分配给哈希环上的下一个服务器。
-
如果有新增的服务器, 那么哈希环上新增服务器之后的那个服务器的部分任务就会被分配给该服务器。
一致性hash的特性
- 单调性(Monotonicity)
- 如果已经有一些请求通过哈希分派到了相应的服务器进行处理,又有新的服务器加入到系统中时候,应保证原有的请求可以被映射到原有的或者新的服务器中去,而不会被映射到原来的其它服务器上去。
- 分散性(Spread)
- 分布式环境中,客户端请求时候可能不知道所有服务器的存在,可能只知道其中一部分服务器,在客户端看来他看到的部分服务器会形成一个完整的hash环。
- 如果多个客户端都把部分服务器作为一个完整hash环,那么可能会导致,同一个用户的请求被路由到不同的服务器进行处理。
- 这种情况显然是应该避免的,因为它不能保证同一个用户的请求落到同一个服务器。
- 所谓分散性是指上述情况发生的严重程度。、
- 好的哈希算法应尽量避免尽量降低分散性。 一致性hash具有很低的分散性
- 平衡性(Balance)
- 平衡性也就是说负载均衡,是指客户端hash后的请求应该能够分散到不同的服务器上去。
- 一致性hash可以做到每个服务器都进行处理请求,但是不能保证每个服务器处理的请求的数量大致相同。
- 就比如说上图, 没有添加node5时, node4处理了50%的用户请求, 这样的现象就是一致性hash的倾斜。解决方案为虚拟节点。
虚拟节点
- 当服务器节点比较少的时候会出现一致性hash倾斜的问题。
- 解决方案一: 多加机器(也就是说大公司有钱任性, 小公司才会发生的问题...)
- 解决方案二: 加虚拟节点
- 发现哪一个服务器的负载比较多, 就在其与其之前的服务器上添加虚拟节点。
- 当物理机器数目为M,虚拟节点为N的时候,实际hash环上节点个数为M*(N + 1)。
均匀一致性hash
- 如果生成虚拟节点的算法不够好, 可能也不能把任务均匀分配, 所以就需要均匀一致性hash算法
- 均匀一致性hash的目标是如果服务器有N台,客户端的hash值有M个,那么每个服务器应该处理大概M/N个用户的。
- 就比如说上图, N = 5, M = 10, 那么最理想的情况就是每台服务器处理2个请求, 但是既然已经倾斜了, 可以考虑的是在node2和node5之间再加一个node3的虚拟节点(因为它的任务比较少)。
总结
- 在分布式系统中一致性hash起着不可忽略的地位,无论是分布式缓存,还是分布式Rpc框架的负载均衡策略都有所使用