互联网分布式系统中,很多服务是数据存储相关的,海量访问量下,直接访问存储介质是抗不住的,需要使用cache,cache集群的负载均衡算法就成为一个重要的话题,这里对现有的负载均衡算法进行一些总结。
BTW:虽然是Cache负载均衡算法小结,其实可以说是负载均衡算法小结,只是针对Cache应用场景罢了。
负载均衡算法主要有:
Static算法
Random算法
Round robin算法
Hash算法
CARP算法
Consistent hash算法
Static算法
负载均衡的石器时代,为一个服务指定多个IP:PORT, 备份模式,其总是返回服务器组的第一个服务器(只要第一个服务器可用),当第一个服务器没有用的时候,才会返回后续可用的服务器。
这种情况下,每台机器都包括全量的数据,查询通常会落到第一台机器上,第一台机器上Cache命中率高,但是当失败的时候,落到第二胎机器上,那就杯具了,Cache命中率那个低啊!!!
Random算法
地球人都知道的算法,对于无状态服务比较适用,随便选取一台机器就可以。
idx=rand()%M
在实际使用中,跟Static算法一样,都是模块维护全量数据,这个还好每台机器的cache命中率理论上应该差不多,但是都不高,为啥呢?因为同样一个请求一会落到机器A,一会落到机器B上。败家子啊,浪费内存啊,内存有限,Cache会被淘汰,频繁淘汰,当然使得命中率低下啊。
Round robin算法
典型的平均主义,吃大锅饭的,皇帝轮流做啊,顺序依次选取服务器。
idx=(idx+1)%M
同样的模块维护全量数据,跟Random一样杯具,基本上一样的原因。相同的请求会被落到不同的机器上,导致Cache命中率低。
Hash算法
又叫取余算法,将query key做hash之后,按照机器数量取余,选取中一个机器进行连接服务。
idx=hash(query_key)%M
余数计算的方法简单,数据的分散性也相当优秀,但也有其缺点。那就是当添加或移除服务器时,缓存重组的代价相当巨大。添加服务器后,余数就会产生巨变,这样就无法获取与保存时相同的服务器,从而影响缓存的命中率。
CARP算法
CARP准确的说不是一个算法,而是一个协议,Cache Array Routing Protocol,Cache群组路由协议。
计算全部服务器的idx_key=hash(query_key+server_idx),其中计算得到idx_key最大的server_idx就是需要的idx。
假设开始3台后端服务器,请求用标志串 req = "abcd" 来标志,服务器用 S1, S2, S3来标志, 那么,通过对 req + Sx 合并起来计算签名就可以对每个服务器得到一个数值:
(req = "abcd" + S1) = K1
(req = "abcd" + S2) = K2
(req = "abcd" + S3) = K3
计算的方法可以使用crc,也可以使用MD5,目的的得到一个*散列*的数字,这样在K1,K2,K3中 必定有一个最大的数值,假设是K2,那么可以将请求req扔给S2,这样,以后对相同的请求, 相同的服务器组,计算出来的结果必定是K2最大,从而达到HASH分布的效果。
巧妙的地方在于,新增或者删除一台服务器的时候,不会引起已有服务器的cache大规模失效, 假设新增一台服务器S4,那么对S1,S2,S3计算的K值都完全相同,那么对S4可以计算得到一个新值K4,如果计算K的算法足够散列,那么原先计算到 S1,S2,S3的请求,理论上都会有1/4的请求新计算得到的K4比原先的K大, 那么这1/4的请求会转移到S4,从而新增的S4服务器会负担1/4的请求,原先的S1,S2,S3也只会负担原先的3/4。
(req = "abcd" + S2) = K2
(req = "abcd" + S3) = K3
计算的方法可以使用crc,也可以使用MD5,目的的得到一个*散列*的数字,这样在K1,K2,K3中 必定有一个最大的数值,假设是K2,那么可以将请求req扔给S2,这样,以后对相同的请求, 相同的服务器组,计算出来的结果必定是K2最大,从而达到HASH分布的效果。
巧妙的地方在于,新增或者删除一台服务器的时候,不会引起已有服务器的cache大规模失效, 假设新增一台服务器S4,那么对S1,S2,S3计算的K值都完全相同,那么对S4可以计算得到一个新值K4,如果计算K的算法足够散列,那么原先计算到 S1,S2,S3的请求,理论上都会有1/4的请求新计算得到的K4比原先的K大, 那么这1/4的请求会转移到S4,从而新增的S4服务器会负担1/4的请求,原先的S1,S2,S3也只会负担原先的3/4。
Consistent hash算法
一致性hash算法是:首先求出服务器(节点)的哈希值,并将其配置到0~2^32的圆(continuum)上。然后用同样的方法求出存储数据的键的哈希值,并映射到圆上。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过2^32仍然找不到服务器,就会保存到第一台服务器上。
idx=FirstMaxServerIdx(hash(query_key))
![](http://blogimg.chinaunix.net/blog/upfile2/110505185953.png)
添加节点的时候:
![](http://blogimg.chinaunix.net/blog/upfile2/110505190049.png)
consistent hash算法背后最基础的思想就是:
对object和cache machine使用相同的hash函数
【DHT算法的核心啊,P2P的理论基石啊,资源和地址节点在统一地址空间进行编址】。
Consistent Hash适用于每个节点只保存部分数据,而不是像前面几种算法,每个节点保存全量数据
。这样做的好处是能够把cache机器映射到一段interval 上,而这段interval就会包含一定数目的对象的hash值。如果某台cache机器被移除了,那么它映射到的interval被和它相邻的一个 cache机器托管,其他所有的cache机器都不用变。
一致性哈希算法最大程度的避免了key在服务节点列表上的重新分布,其他附带的改进就是有的一致性 哈希算法还增加了虚拟服务节点的方法,也就是一个服务节点在环上有多个映射点,这样就能抑制分布不均匀,最大限度地减小服务节点增减时的缓存重新分布。
参考: