# 一致性Hash算法


<!--more-->

[演示代码](https://gitee.com/ixinglan/cluster-demo.git)

**分布式和集群**

分布式和集群是不一样的，分布式一定是集群，但是集群不一定是分布式（因为集群就是多个实例一起工作，分布式将一个系统拆分之后那就是多个实例；集群并不一定是分布式，因为复制型的集群不是拆分而是复制）

![Hash算法示意](https://img.zhaojq.top/img.png "Hash算法示意")

**Hash算法**

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

> Hash算法较多的应用在数据存储和查找领域，最经典的就是Hash表，它的查询效率非常之高，其中的哈希算法如果设计的比较ok的话，那么Hash表的数据查询时间复杂度可以接近于O(1)
>
> 顺序查找法：通过循环完成，效率不高
>
> 二分查找：排序后折半查找，会提高一些效率
>
> 数组直接寻址法：一次查找速度快，但浪费空间
>
> 开放寻址法：实际就是Hash方式，但可能导致hash冲突
>
> 拉链法：也是hash方式，数组存储位置放置一个链表，hash冲突时存储到链表里

以上hash形式存储的效率取决于**hash算法**，算法能够让数据平均分布，既能够节省空间又能提高查询效率。Hash算法的研究是很深的一门学问，比较复杂，长久以来，Hash表内部的Hash算法也一直在更新，很多数学家也在研究。

## 一、Hash算法应用场景

Hash算法在分布式集群架构中的应用场景
Hash算法在很多分布式集群产品中都有应用，比如分布式集群架构Redis、Hadoop、ElasticSearch， Mysql分库分表，Nginx负载均衡等
主要的应用场景归纳起来两个

- 请求的负载均衡（比如nginx的ip_hash策略）

  使用哈希算法，事情就简单很多，我们可以对ip地址或者sessionid进行计算哈希值，哈希值与服务器数量进行取模运算，得到的值就是当前请求应该被路由到的服务器编号，如此，同一个客户端ip发送过来的请求就可以路由到同一个目标服务器，实现会话粘滞。

- 分布式存储

  以分布式内存数据库Redis为例,集群中有redis1，redis2，redis3 三台Redis服务器
  那么,在进行数据存储时,<key1,value1>数据存储到哪个服务器当中呢？针对key进行hash处理hash(key1)%3=index, 使用余数index锁定存储的具体服务器节点

## 二、普通hash算法存在的问题

普通Hash算法存在一个问题，以ip_hash为例，假定下载用户ip固定没有发生改变，现在tomcat3出现了问题，down机了，服务器数量由3个变为了2个，之前所有的求模都需要重新计算。

如果在真实生产情况下，后台服务器很多台，客户端也有很多，那么影响是很大的，缩容和扩容都会存在这样的问题，大量用户的请求会被路由到其他的目标服务器处理，用户在原来服务器中的会话都会丢失。

## 三、一致性hash算法

思路:

![一致性Hash环](https://img.zhaojq.top/img_1.png "一致性Hash环")

> 首先有一条直线，直线开头和结尾分别定为为1和2的32次方减1，这相当于一个地址，对于这样一条线，弯过来构成一个圆环形成闭环，这样的一个圆环称为hash环。我们把服务器的ip或者主机名求hash值然后对应到hash环上，那么针对客户端用户，也根据它的ip进行hash求值，对应到环上某个位置，然后如何确定一个客户端路由到哪个服务器处理呢？按照顺时针方向找最近的服务器节点

![服务器下线](https://img.zhaojq.top/img_2.png "服务器下线")

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

![增加服务器](https://img.zhaojq.top/img_3.png "增加服务器")

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

![数据倾斜问题](https://img.zhaojq.top/img_4.png "数据倾斜问题")

> 1）如前所述，每一台服务器负责一段，一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据，具有较好的容错性和可扩展性。
> 但是，一致性哈希算法在服务节点太少时，容易因为节点分部不均匀而造成数据倾斜问题。例如系统中只有两台服务器，其环分布如下，节点2只能负责非常小的一段，大量的客户端
> 请求落在了节点1上，这就是数据（请求）倾斜问题
> 2）为了解决这种数据倾斜问题，一致性哈希算法引入了虚拟节点机制，即对每一个服务节点计算多个哈希，每个计算结果位置都放置一个此服务节点，称为虚拟节点。
> 具体做法可以在服务器ip或主机名的后面增加编号来实现。比如，可以为每台服务器计算三个虚拟节点，于是可以分别计算 "节点1的ip#1"、"节点1的ip#2"、"节点1的ip#3"、"节点2的ip#1"、"节点2的ip#2"、"节点2的ip#3"的哈希值，于是形成六个虚拟节点，当客户端被路由到虚拟节点的时候其实是被路由到该虚拟节点所对应的真实节点

![虚拟节点](https://img.zhaojq.top/img_5.png "虚拟节点")

## 四、一致性hash算法实现

**示例代码：** ![数据倾斜问题](https://img.zhaojq.top/img_4.png "数据倾斜问题") **hash-demo**

## 五、nginx配置一致性hash负载均衡策略

**ngx_http_upstream_consistent_hash** 模块是一个负载均衡器，使用一个内部一致性hash算法来选择合适的后端节点。是一个第三方模块，需要我们下载安装后使用. 

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

- consistent_hash `$remote_addr`：可以根据客户端ip映射
- consistent_hash `$request_uri`：根据客户端请求的uri映射
- consistent_hash `$args`：根据客户端携带的参数进行映

1. nginx目录下载并解压： [https://github.com/replay/ngx_http_consistent_hash](https://github.com/replay/ngx_http_consistent_hash)

2. 执行如下命令`configure --add-module=/root/ngx_http_consistent_hash-master`，`make`，`make install`

3. nginx.conf 中配置即可

   ```nginx
   upstream demoServer {
     consistent_hash $request_uri;
     server 127.0....;
     server 127.0....;
   }
   ```

