一致性Hash问题及解决方案

2,806 阅读5分钟

文章内容输出来源:拉勾教育Java高薪训练营

Hash算法应⽤场景

  • Hash算法在分布式集群架构中的应⽤场景

Hash算法在很多分布式集群产品中都有应⽤,⽐如分布式集群架构Redis、Hadoop、ElasticSearch, Mysql分库分表,Nginx负载均衡等。

  • 主要的应用场景归纳起来有两个:
    • 请求的负载均衡(⽐如nginxip_hash策略)
     Nginx的ip_hash策略,可以在客户端IP不变的情况下,将其发出的请求路由到同一台目标服务器,
     实现会话粘滞,避免处理session共享问题。
     
     若没有ip_hash策略,要如何实现会话粘滞呢?
     
     比如:可以维护一张客户端IP或者sessionid和具体目标服务器之间映射关系的映射表,形如`<ip,tamcat1>`。
     
     这样做有什么缺点呢?
     
     1)在客户端数量庞大的情况下,映射表也会非常大,浪费存储空间
     2)客户端上下线,⽬标服务器上下线,都会导致重新维护映射表,映射表维护成本很⼤
    
    • 分布式存储
     以分布式集群架构Redis为例,集群中有redis1,redis2,redis3 三台Redis服务器,
     那么存取数据需要锁定哪一台Redis服务器呢?
     
     比如:数据 <key1,value1> 
     在存储时,可以针对key进⾏hash处理,hash(key1)%3=index, 使⽤余数index锁定存储的具体服务器节点,存入数据;
     在取值时,同样针对key进⾏hash处理,hash(key1)%3=index,使⽤余数index锁定取值的具体服务器节点,取出数据;
    

普通Hash算法存在的问题

普通Hash算法存在⼀个问题,以ip_hash为例,集群中有tomcat1,tomcat2,tomcat3 三台Tomcat服务器,

假定客户端ip固定没有发⽣改变,现在tomcat3出现问题宕机了,服务器数量由3个变为了2个,之前所有的求模都需要重新计算。

普通Hash算法存在的问题示意图

在真实的生产环境下,若后台服务器有许多台,客户端数量也非常庞大的情况下,影响是非常大的;

服务器缩容/扩容都会发生这样的问题,大量的客户端请求被路由到其他的服务器,客户端在原来服务器的会话都会丢失。

那么如何解决这样的问题呢?

答案就是接下来要介绍的一致性Hash算法

一致性Hash算法

  • 一致性Hash算法的设计思路 一致性Hash算法示意图 首先要有一条直线,直线的开头和结尾分别定义为02的31次方减1,将这样一条直线弯曲形成一个圆形的闭环,这个圆环称为Hash环

    针对服务器,把服务器的ip或者主机名求hash值然后对应到hash环上;
    针对客户端⽤户,也根据它的ip进⾏hash求值,对应到环上某个位置;

    在hash环上按照顺时针⽅向找最近的服务器节点,即可确定⼀个客户端请求路由到哪个服务器处理。 Hash环示意图

  • 服务器缩容情况下一致性Hash算法的表现

    假如服务器3下线了,那么原来路由到服务器3上的客户端,按照顺时针查找最近的服务器,会被路由到服务器4,其他的客户端请求没有受到影响;

    请求的迁移达到了最⼩,这样的算法对分布式集群来说是⾮常合适的,避免了⼤量请求迁移。 一致性Hash算法缩容示意图

  • 服务器扩容情况下一致性Hash算法的表现

    增加了服务器5之后,原来路由到服务器3的部分客户端被路由到服务器5,其他的客户端请求没有受到影响;

    请求的迁移达到了最⼩,这样的算法对分布式集群来说是⾮常合适的,避免了⼤量请求迁移。 一致性Hash算法扩容示意图

  • 一致性Hash算法的优点

    在一致性Hash算法实现过程中,每⼀台服务器负责⼀段,⼀致性哈希算法对于节点的增减都只需重定位环空间中的⼀⼩部分数据,具有较好的容错性和可扩展性。

一致性Hash算法存的的问题及解决方案

前面讲了这么多一致性Hash算法的优点,那么一致性Hash算法有没有缺点呢?
其实是有的。

  • 数据(请求)倾斜问题

    ⼀致性哈希算法在服务节点太少时,容易因为节点分部不均匀⽽造成数据倾斜问题。
    例如系统中只有两台服务器,其环分布如下,节点2只能负责⾮常⼩的⼀段,⼤量的客户端请求落在了节点1上,这就是数据(请求)倾斜问题一致性Hash算法+虚拟节点方案示意图

  • 解决方案

    为了解决数据倾斜问题,一致性Hash算法引入了虚拟节点机制,即对每⼀个服务节点计算多个哈希,每个计算结果位置都放置⼀个此服务节点,称为虚拟节点。

    具体做法可以在服务器ip或主机名的后⾯增加编号来实现。
    ⽐如,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “节点1的ip#1”、“节点1的ip#2”、“节点1的ip#3”、“节点2的ip#1”、“节点2的ip#2”、“节点2的ip#3”的哈希值,于是形成六个虚拟节点。
    当客户端被路由到虚拟节点的时候其实是被路由到该虚拟节点所对应的真实节点

Nginx 配置⼀致性Hash负载均衡策略

ngx_http_upstream_consistent_hash 模块是⼀个负载均衡器,使⽤⼀个内部⼀致性hash算法来选择合适的后端节点。

该模块可以根据配置参数采取不同的⽅式将请求均匀映射到后端机器:

  • consistent_hash $remote_addr:可以根据客户端ip映射
  • consistent_hash $request_uri:根据客户端请求的uri映射
  • consistent_hash $args:根据客户端携带的参数进⾏映

其中比较常用的是前两种。

ngx_http_upstream_consistent_hash 模块是⼀个第三⽅模块,需要我们下载安装后使⽤。

  • github下载nginx⼀致性hash负载均衡模块 点击去下载 nginx⼀致性hash负载均衡模块下载页面

  • 将下载的压缩包上传到nginx服务器,并解压

  • 进⼊当时nginx的源码⽬录,执⾏如下命令

    ./configure —add-module=/root/ngx_http_consistent_hash-master
    make
    make install
    
  • Nginx就可以使⽤啦,在nginx.conf⽂件中配置