常见限流算法有哪些?固定窗口、滑动窗口、漏桶、令牌桶有什么区别?
简化版
常见限流算法有固定窗口、滑动窗口、漏桶和令牌桶。固定窗口实现简单,但窗口边界可能出现流量突刺;滑动窗口统计更平滑,精度更高但成本更大;漏桶按固定速率流出,适合削峰整形,但对突发流量不友好;令牌桶按固定速率生成令牌,请求拿到令牌才通过,允许一定突发,是实际系统里很常用的限流算法。
详细版
固定窗口把时间切成固定区间,比如每秒最多 100 次,实现简单,但在两个窗口交界处可能短时间通过 200 次。滑动窗口把统计范围滚动起来,可以用更细粒度小窗口或请求时间戳实现,能缓解边界突刺。
漏桶把请求放入桶中,以固定速度处理,多余请求被拒绝或排队。它让出口速率稳定,适合保护下游,但突发流量会被抹平。令牌桶以固定速度生成令牌,桶满后丢弃新令牌;请求消耗令牌才能通过。它既限制平均速率,又允许桶内令牌支持短时突发。
面试可以按“实现复杂度、是否允许突发、是否平滑、适用场景”比较。入口保护和 API 限流常用令牌桶或滑动窗口,严格整形场景可用漏桶。
完整版教学
一、限流要解决什么
限流的目标是让系统只接受自己能承受的流量。分布式系统中,流量可能来自用户突增、爬虫、恶意攻击、上游重试、大促活动或批处理任务。如果没有限流,过量请求会打满线程池、连接池、数据库和缓存,最终让正常用户也不可用。
限流不是为了让所有请求成功,而是为了在过载时有控制地失败。好的限流会保护核心资源,把故障从“全站慢死”变成“部分请求快速失败或排队”。面试时要先讲这个目标,再比较算法。
二、固定窗口算法
固定窗口把时间切成固定区间,比如 1 秒一个窗口,每个窗口最多允许 100 个请求。实现上只需要记录当前窗口开始时间和计数器,超过阈值就拒绝。它非常简单,适合低成本场景。
问题是边界突刺。假设第 1 秒末通过 100 个请求,第 2 秒初又通过 100 个请求,那么在极短时间内可能放行 200 个请求,实际峰值远超限制。对下游脆弱的系统,这个突刺可能足以造成压力尖峰。
三、滑动窗口算法
滑动窗口把统计周期变成滚动范围。实现方式可以是记录请求时间戳,也可以把 1 秒拆成多个小格,比如 10 个 100ms 小窗口,每次统计最近 10 个小窗口之和。这样窗口边界会更平滑,突刺问题明显缓解。
滑动窗口的代价是实现和存储更复杂。小窗口越细,统计越准确,维护成本越高;时间戳方式更精确,但高并发下内存和清理成本更大。工程上常用分桶滑动窗口,在精度和性能之间折中。
四、漏桶算法
漏桶可以想象成请求先进桶,桶以固定速率向外漏水。无论入口流量多么抖动,出口速率都稳定。桶满后,新请求要么被拒绝,要么排队等待。漏桶非常适合流量整形,让下游看到稳定速率。
它的缺点是不太允许突发。即使系统当前很空闲,只要出口速率固定,突发请求也会被排队或拒绝。对用户请求来说,这可能造成不必要延迟;对需要严格保护下游速率的场景,比如调用第三方接口配额,漏桶就比较合适。
五、令牌桶算法
令牌桶按固定速度生成令牌,放进容量有限的桶里。请求到来时,必须拿到令牌才能通过;拿不到就拒绝或排队。桶满后新令牌丢弃,所以长期平均速率受生成速度限制;桶内已有令牌又允许短时间突发。
这使令牌桶非常适合互联网接口限流。比如每秒生成 100 个令牌,桶容量 200,系统平均每秒最多 100 请求,但如果前一段时间流量低,桶里攒了令牌,短时间可以处理 200 个突发请求。它比漏桶更符合真实业务流量的弹性需求。
六、面试追问与工程边界
常见追问是限流后怎么处理请求。可以直接拒绝返回 429 或业务繁忙,也可以排队等待、降级返回、异步削峰。排队不是万能的,队列过长会放大延迟,最终也要限长和超时。
另一个追问是限流阈值怎么定。阈值要基于压测容量、下游瓶颈、P99 延迟、错误率和业务优先级,而不是凭感觉。核心接口可以高优先级,非核心接口限得更严。限流还要配合监控,观察拒绝率是否异常。
七、落地设计清单
选择限流算法时,先判断要保护什么。如果是保护下游稳定速率,漏桶更合适;如果是保护接口平均 QPS 并允许业务突发,令牌桶更合适;如果只是简单防刷,固定窗口成本最低;如果要更公平地限制最近一段时间请求,滑动窗口更合适。算法不是越复杂越好,要看精度、性能和业务容忍度。
限流还要设计被拒绝后的行为。直接返回 429 或业务繁忙最简单,排队能提升成功率但会增加延迟,降级能改善体验但要有业务兜底。队列必须有最大长度和等待超时,否则限流会变成另一个堆积点。对于用户请求,通常更推荐快速失败或短排队;对于后台任务,可以排队削峰或异步处理。
八、常见误区和参数经验
第一个误区是只限制 QPS,不限制并发。一个接口 QPS 不高但每次耗时很长,也可能把线程池占满。因此限流常常要同时看请求速率和并发数。第二个误区是全局一个阈值。不同用户、租户、接口、来源的优先级不同,大客户和普通爬虫不应该共享同一套策略。
参数设置上,要从压测容量和线上延迟分布出发。阈值过低会误伤正常流量,过高挡不住峰值。令牌桶容量决定允许多大的突发,生成速率决定长期平均流量;滑动窗口的小窗口数量决定精度和成本。上线后要观察通过量、拒绝量、排队耗时和下游延迟,持续调参。 还要注意限流位置。客户端限流能减少无效请求,但不可信;网关限流能保护入口,但看不到所有内部调用;服务端本地限流能保护自身资源,但全局视角不足;下游依赖限流能保护数据库、缓存或第三方接口。成熟系统通常是多层限流,而不是只在一个地方放一个算法。
压测后再定阈值。
九、常见误区与追问
这道题不能只背概念,要把「限流算法」放回真实分布式系统里解释:谁发起、谁协调、状态如何变化、失败后怎么兜底,以及它和性能、可用性、一致性的取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 固定窗口简单但边界突刺,滑动窗口更平滑,漏桶恒速输出,令牌桶允许突发 | 不要停在名词解释 |
| 流程机制 | 请求到达 -> 按算法更新窗口或令牌 -> 判断是否超过阈值 -> 通过或拒绝 -> 记录统计用于下一次判断 | 说明触发方、参与方、状态变化和兜底 |
| 工程取舍 | 令牌桶速率 50 QPS、桶容量 100 时,长期 50 QPS,但能承受最多 100 个瞬时突发 | 治理组件是为了控制故障半径,不是让下游无限扛流量 |
限流算法 面试拆解:
1. 请求到达
2. 按算法更新窗口或令牌
3. 判断是否超过阈值
4. 通过或拒绝
5. 记录统计用于下一次判断
记忆钩子:先定位治理目标,再拆限流、熔断、降级、重试、追踪、灰度和幂等边界;回答时要紧扣「限流算法」这道题,不要把相邻概念混成一段泛泛的分布式套话。
- 误区:固定窗口没有问题。 窗口边界可能在短时间内放过两倍流量。
- 误区:漏桶和令牌桶一样。 漏桶平滑输出,令牌桶允许突发。
- 误区:滑动窗口一定最适合。 精确滑动窗口成本更高,工程上常用滑动窗口计数折中。
- 追问:网关常用什么算法? 令牌桶、滑动窗口和 Redis Lua 原子计数都很常见。
- 追问:如何处理突发流量? 令牌桶可允许短突发,同时限制长期平均速率。
- 追问:如何避免热点 key? 本地预热令牌、分片 key、限流服务或按维度拆分。
十、加强记忆
固定窗口简单但有边界突刺;滑动窗口更平滑但成本更高;漏桶固定速率流出,适合整形;令牌桶限制平均速率且允许突发,最常用。回答时围绕“是否平滑、是否允许突发、实现成本、适用场景”比较,面试官会觉得很清楚。