分布式系统中,多个节点间实现请求的负载均衡, 如果部分实例由于一些特殊的原因宕机或者需要扩容,这个时候就涉及到部分请求重新分发的问题。
为了最小化请求重新分发,引入了一致性 hash 算法。
先看下传统的 hash 算法解决请求分发问题的过程。
hash
hash 算法大家应该很熟悉了,就是将不定长的数据经过计算后输出定长的结果。
如果在分布式系统中使用 传统hash 方法来分发请求,
server = hash(key) % n
这会带来两个问题:
-
一旦服务器节点数 n 发生伸缩,就需要重新计算所有 hash ,会大大的消耗服务器性能从而降低整个系统的吞吐量。
-
可能出现分布不均匀问题,部分节点负载过高,部分节点负载过低。
为了解决传统 hash 方法带来的问题,1997年麻省理工提出了一致性 hash 算法( 也称hash 环)。
一致性 hash
也称作 hash 环,一致性哈希是一种特殊的哈希技术,当哈希表的大小发生变化时,平均只需要重新映射n/m个键,其中n是键的数量,m是插槽的数量。
具体工作过程:
-
请求的 key 进行 hash 计算后放入环中;
-
服务器 ip 进行 hash 计算后放入环中;
-
将 hash(key) 和 hash(ip) 相同的请求分发到对应的服务器,如果找不到相同的值,则按照顺时针方向找到最近的节点来处理请求。
注意,这里的hash 计算已经和节点数无关了,2^32 表示的是环上虚拟节点的个数。
position = hash(key) % (2^32 ).
这个时候,服务器节点数发生伸缩,只需要重新做部分计算即可。
在0号机器和1号机器间添加新的节点4号机器时,只需要重新计算 k1 和 k2 的hash 即可。
如果发生宕机,假设是2号机器宕机,这个时候执行需要将之前分发在2号机器的k3重新分发到 0号机器即可。
最后,为了解决请求分布不均匀问题,可以为每台服务器增加多个虚拟节点随机hash 到不同的位置 , 环中的服务器节点数越来越多,请求的分发也就越来越均匀。
应用
分布式系统经常会用到一致性hash,比如 cassandra 集群中 客户端请求分发过程、分布式缓存:redis、memcached 他们的客户端包已经封装了一致性hash算法,比如 jedis。
值得一提的是, redis 3.0 后当redis 作为数据存储使用时,采用了hash 槽的方式(见之前的文章《Redis 高可用》)。