负载均衡常见算法和它们的取舍

负载均衡就是把请求合理分给多台服务器。轮询、随机、加权、最少连接、哈希,各有各的活法。这篇讲清几种主流算法怎么实现,重点说说最常用的轮询和随机到底有哪些坑,帮你别再无脑用默认策略。

一台服务器扛不住,就多上几台。可请求来了,到底该发给哪一台?这就是负载均衡要解决的事。

把请求“合理地”分给后面的多台机器,说起来简单,但“合理”俩字里藏着好几种算法,各有各的脾气。

这篇我们把常见的几种捋一遍,讲讲怎么实现,重点是把用得最多的轮询和随机的坑说透——很多人默认用它俩,却不知道它们坑在哪。

轮询 挨个来

最简单、最直觉的一种:轮流分配。

第一个请求给 A,第二个给 B,第三个给 C,第四个又回到 A,如此循环。雨露均沾,谁也不多谁也不少。

实现也简单到家,一个计数器取模就行:

服务器列表: [A, B, C]

请求1 -> index = 0 % 3 = 0 -> A
请求2 -> index = 1 % 3 = 1 -> B
请求3 -> index = 2 % 3 = 2 -> C
请求4 -> index = 3 % 3 = 0 -> A
...
​

维护一个自增的下标,每来一个请求 下标 % 服务器数量,取到谁就发给谁。

简单、公平、无脑。但它有个大前提,后面会说它坑在哪。

加权轮询 能者多劳

轮询假设每台机器一样强。可现实里机器配置常常参差不齐:有的 16 核,有的才 4 核。让它俩接一样多的请求,显然不公平。

加权轮询就是给每台机器配个权重,配置高的权重大,分到的请求就多。

A(权重 4)  B(权重 2)  C(权重 1)

每 7 个请求里,大致按 4:2:1 分:
A A A A  B B  C
​

权重 4 的 A 每轮拿 4 个,权重 1 的 C 只拿 1 个。能者多劳,按能力分配,比无脑均分合理多了。

随机 抛骰子

另一种更省事的:随机挑一台。

每来一个请求,从列表里随机抓一个出来。不用维护下标,不用记状态。

服务器列表: [A, B, C]
请求来了 -> 随机数 -> 撞上谁就是谁
​

它的好处是无状态、实现极简。而且根据大数定律,请求量足够大时,随机分配的结果长期看也接近均匀。

随机也能加权,跟加权轮询一个道理,权重大的被抽中的概率大。

重点 轮询和随机的坑

轮询和随机是最常用的两个,但它们有个共同的致命假设:

它们默认每个请求的“分量”都一样,每台机器处理起来都一样快。

可现实根本不是这样。这个假设一破,坑就来了。

坑一:不管后端死活。

轮询和随机只按顺序或概率分,压根不看后端现在忙不忙、还活不活。

假设 C 这台机器卡住了、响应巨慢,轮询照样每轮把请求发给它,随机照样有概率抽中它。请求发过去就是石沉大海,白白堆在一台快死的机器上。它俩对后端的真实状态一无所知。

坑二:请求不等价,忙闲不均。

请求有轻有重。有的请求秒回,有的要跑好几秒的大查询。

轮询/随机只管“把请求数量分均匀”,但分均匀数量 ≠ 分均匀压力。

轮询按数量均分:
  A: 请求1(重), 请求4(重)   <- 全是重活,累死
  B: 请求2(轻), 请求5(轻)   <- 全是轻活,闲死
  C: 请求3(轻), 请求6(轻)   <- 全是轻活,闲死

数量都是 2 个,但 A 被重活压垮,B/C 却在摸鱼
​

每台请求数一样,但恰好把重活都堆给了 A,A 忙死、B 和 C 闲死。数量均衡了,负载根本没均衡。

坑三(针对轮询):遇到有状态请求会出问题。

如果应用把用户会话存在了本地(比如登录状态存在 A 机器的内存里),轮询把同一个用户的下一个请求转给了 B,B 上没这个会话,用户就“莫名其妙掉线了”。随机也有同样毛病。

这几个坑说白了就一句:轮询和随机太“天真”,它们只顾公平地分数量,不管后端的真实忙闲和死活。

更聪明的算法 看菜下饭

针对上面的坑,就有了更聪明的算法。

最少连接:谁闲发给谁。

不再机械地轮或随机,而是看哪台机器当前手上的连接数最少,就把新请求发给它。

连接数少,通常意味着它更闲。这就把“后端忙不忙”考虑进来了,直接治了轮询/随机“不看死活”的坑。谁闲发给谁,负载自然更均衡。

哈希:同一来源永远发同一台。

按请求的某个特征(比如来源 IP、用户 ID)算个哈希值,用它决定发给哪台。同一个 IP 每次算出来都一样,就永远落到同一台机器。

hash(用户IP) % 机器数 -> 固定落到某一台

同一个用户 -> 每次都是 A
​

这就解决了轮询“会话飘走”的坑——同一个用户始终打到同一台,本地会话就一直能用。

不过普通哈希有个隐患:机器数量一变(加一台或挂一台),% 机器数 的结果全乱套,几乎所有请求的归属都变了,缓存和会话大面积失效。为了解决这个,就有了一致性哈希,让增减机器时只影响一小部分请求,而不是全盘打乱。这块展开够写一篇了,这里先埋个点。

那到底怎么选

没有万能算法,看场景:

  • 机器配置差不多、请求也都差不多轻 → 轮询或随机就够了,简单省心
  • 机器配置不一 → 加权轮询/加权随机,按能力分
  • 请求有轻有重、后端忙闲差异大 → 最少连接,动态看负载
  • 需要会话保持、或者要打缓存命中 → 哈希 / 一致性哈希,让同源请求固定落点

实践里 Nginx 默认就是轮询,够用的场景很多。但一旦你的请求分量不均、或者后端会卡,就别死守轮询了,该换最少连接换最少连接。

小结

负载均衡这几种算法,各有各的活法:

  • 轮询:挨个轮流,简单公平;加权轮询按机器能力分
  • 随机:抛骰子,无状态省事,量大了也接近均匀
  • 轮询和随机的共同坑:太天真——不看后端死活、不管请求轻重、还可能让会话飘走
  • 最少连接:谁闲发给谁,把后端忙闲考虑进来
  • 哈希 / 一致性哈希:同源固定落点,解决会话保持和缓存命中

一句话选型:均质环境用轮询随机图省事,异构或有状态场景就上最少连接和哈希。

别一律用默认的轮询,先看清你的请求和机器是不是真的“均匀”,再决定用哪个。

更多推荐

章节目录