Nginx upstream_module 一致性哈希避免大量缓存失效

哈希负载均衡原理


ngx_http_upstream_hash_module支持普通的hash及一致性hash两种负载均衡算法,默认的是普通的hash来进行负载均衡。
nginx 普通的hash算法支持配置http变量值作为hash值计算的key,通过hash计算得出的hash值和总权重的余数作为挑选server的依据;nginx的一致性hash(chash)算法则要复杂一些。
 

 

一致性hash算法的原理


无论是基于客户端IP地址Hash算法实现负载均衡的upstream_ip_hash模块还是基于任意关键字Hash算法实现负载均衡的。upstream_hash模块都有一个问题,当我们上游服务器的数目发生变化的时候会导致大量请求的路由策略失效。一致性Hash算法可以缓解该问题。

   一致性hash用于对hash算法的改进,后端服务器在配置的server的数量发生变化后,同一个upstream server接收到的请求会的数量和server数量变化之间会有变化。尤其是在负载均衡配置的upstream server数量发生增长后,造成产生的请求可能会在后端的upstream server中并不均匀,有的upstream server负载很低,有的upstream server负载较高,这样的负载均衡的效果比较差,可能对upstream server造成不良的影响。由此,产生了一致性hash算法来均衡。下面介绍的是upstream_hash模块实现的一致性Hash算法。

 

Hash负载均衡算法带来的问题


 

Nginx upstream_module 一致性哈希避免大量缓存失效

这里做个简化,将客户端的key和hash算法都简化。正常情况下没有宕机的时候客户端的key=5,6,7,8,9通过hash算法key%5对应到上游服务器server0,1,2,3,4。通过key%5的算法决定的客户端的请求具体的落到了上游服务器哪台server上面。(不过这是以简化的方式来说明问题,通俗易懂)

Nginx upstream_module 一致性哈希避免大量缓存失效

可以看到当一台server宕机以后,比如下线了一台server4,或者扩容了一台server5。都会导致以前的key它的路由的server发生变化,比如下线了server4,那么hash算法就变成了key%4,之前的算法key%5就失效了。导致key=5的客户端原本路由到server0上变成了路由到server1,同理增加一台后果也类似。这样的结果是在上游服务有缓存的情况下会导致缓存大量失效,特别面对的是大流量的场景下面,某一台server的缓存经常被命中使得该server能够服务于这么多的用户。由于更改了hash算法以及上游服务server的数量会导致一瞬间所有的server缓存几乎都失效了,这样会引发恶性的连锁反应!!!!!

如何去解决这样的问题呢?这个就需要通过一致性hash算法来解决

 

一致性Hash算法避免大量缓存失效


通过一致性Hash算法可以缓解我们扩容或者宕机的时候我们的路由不要大规模的发生变化,当我们上游服务是使用缓存的时候,不会导致大范围的缓存失效。当然它并不能解决扩容或者宕机之后所有节点的路由都不会发生变化,这个是解决不了的,当然也没有必要去解决。当我们上游应用含有缓存的时候一致性Hash算法可以避免大量的缓存失效。

那么为什么一致性hash算法能改善这种情况呢?

Nginx upstream_module 一致性哈希避免大量缓存失效

  上图表明了产生的hash值与server之间的关系,首先计算出虚拟节点的hash值 ,这里的hash值在配置初始化的时候就已经是计算完成了的。这里的key值就是上图中的对象,机器就是配置的upstream server节点。 这样在计算配置key值生成的hash之后,通过此hash值映射的位置开始顺时针查找,将upstream server定位到找到的第一个upstream server节点上。
  一致性hash与普通的hash的方式在于 在配置的upstream server的数量发生变化之后,普通的hash计算的方法会对所有访问造成影响,从而使得upstream server对于请求的负载可能会不均匀;而一致性hash通过上述的处理,upstream server的数量变化只会影响计算出key值hash与其”最近”的预分配的虚拟节,这里起到的作用是将配置upstream server数量变化造成的整体hash计算的影响收敛到了局部。这样,就算配置的upstream server的数量发生了变化,对负载整体而言,请求发送到upstream server的分布还是均匀的。