Developer. Designer

一致性Hash问题及解决方案

  • 分布式:把一个系统拆分为多个子系统,每个子系统负责格子的那部分功能,独立部署,各司其职
  • 集群:多个实例共同工作,最简单常见的集群是把一个应用复制多份部署 分布式一定是集群,但是集群不一定是分布式

Hash算法,在安全加密领域MD5、SHA等加密算法,在数据存储和查找方面有Hash表,以上都用到了Hash算法

为什么需要使用Hash?

Hash算法较多的应用在素具存储和查找领域,最经典的就是Hash表,它的查询效率非常之高,其中的Hash算法如果设计的比较OK的话,那么Hash表的数据查询时间复杂度可以接近于O(1) 提供一组数据1,5,7,6,3,4,8

Hash算法应用场景

分布式集群 Redis、Hadoop、MySQL分库分表、Nginx负载均衡、ElasticSearch 归纳起来有两个:

  • 请求的负载均衡(Nginx的ip_hash策略) Nginx的ip_hash策略可以在客户端ip不变的情况下,将其发出的请求始终路由到同一个目标服务器上,实现会话粘滞,避免处理session共享问题 如果没有ip_hash策略,如何实现会话粘滞? 可以维护一张映射表,存储客户端ip挥着sessionId与具体目标服务器的映射关系:<ip,server> 缺点:
    1. 客户端很多的情况下,映射表分长达,浪费内存空间
    2. 客户端上下线、目标服务器上下线,都会导致重新维护映射表,映射表维护成本很大 如果使用Hash算法,我们可以对IP地址挥着sessionID进行计算哈希值,哈希值与服务器数量进行取模运算,得到的值就是当前请求应该被路由到的服务器编号,由此,同一个客户端IP发送过来的请求就可以路由到同一个目标服务器,实现会话粘滞。
  • 分布式存储 Redis1、Redis2、Redis3 三台Redis服务器 <key1,value1>数据存储到哪个服务器当中呢? 针对key进行Hash处理 hash(key)%3 = index,使用余数index锁定存储的具体服务器节点

普通Hash算法存在的问题

普通Hash算法存在一个问题,以IP_Hash为例,假定下载用户IP固定没有发生变化,当前后端服务器有一个宕机,服务器数量由3变成了2,那么之前所有用户的求模都需要重新计算 如果在真实生产情况中,后台服务器很多台,客户端也很多台,出现这种问题的影响是很大的,缩容和扩容都会存在这样的问题,大量用户的请求将被路由到其他的目标服务器处理,用户在原来服务器的会话都将丢失。

一致性Hash算法

一致性Hash算法思路

  1. 首先有一条直线,直线开头和结尾分别定为1和2的32次方-1,这相当于一个地址。
  2. 对于这样一条直线,首尾相连构成一个闭环,这样的圆环称为Hash环。
  3. 把服务器IP或者主机名求Hash值然后对应到Hash环上,针对客户端用户,也可以根据它的IP进行Hash求值,对应到环上的某个位置,然后按照顺时针的方向找最近的服务器节点。

缩容

  • 假如将服务器3下线,原来路由到3的客户端请求重新路由到服务器4,对于其他客户端没有影响,只是这一小部分受到影响(请求的迁移量达到了最小,这样的算法对于分布式集群来说非常合适,避免了大量请求迁移)

扩容

  • 增加服务器5之后,原来路由到3的部分客户端路由到新增服务器5上,于其他客户端没有影响,只是这一小部分受到影响(请求的迁移量达到了最小,这样的算法对于分布式集群来说非常合适,避免了大量请求迁移)

存在的问题及解决方法

如上所述,每一台服务器负责一段,一致性Hash算法对于节点的增减都只需重定位环空间的一小部分数据,具有较好的容错性和可扩展性。

  1. 但是,一致性Hash算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题。例如系统中只有两台服务器,其环分布如下,节点2只能负责非常小的一段,大量的客户端请求落在了节点1上,这就是数据倾斜问题。
  1. 为了解决这种数据倾斜问题,一致性Hash算法引入了虚拟节点机制,即对每一个服务节点计算多个Hash,每个计算结果都放置一个此服务节点,称为虚拟节点。 具体做法可以在服务器IP或者主机名后增加编号来实现。比如,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “节点1的ip#1” “节点1的ip#2” “节点1的ip#3”… 于是形成6个虚拟节点,当客户端被路由到虚拟节点的时候其实是被路由到该虚拟节点所对应的真实节点。