限流算法对比指南

目的: 帮助您快速选择合适的限流算法
更新: 2026-02-05


📚 目录


📊 快速对比

算法精确度内存速度突发支持适用场景推荐度
滑动窗口⭐⭐⭐⭐⭐❌ 不允许API限流、登录保护⭐⭐⭐⭐⭐
固定窗口⭐⭐⚠️ 边界突发高并发、低精度⭐⭐⭐
令牌桶⭐⭐⭐⭐✅ 允许API网关、批量操作⭐⭐⭐⭐
漏桶⭐⭐⭐⭐⚠️ 平滑处理流量整形、保护后端⭐⭐⭐⭐

1. 滑动窗口 (Sliding Window)

适用场景

强烈推荐

  • API限流(RESTful API)
  • 登录保护(防暴力破解)
  • 敏感操作(修改密码、支付等)
  • 需要精确控制的场景

不推荐

  • 超高并发场景(内存开销大)
  • 允许短时突发的场景

配置示例

const { RateLimiter } = require('flex-rate-limit');

// 登录保护:15分钟最多5次
const loginLimiter = new RateLimiter({
  algorithm: 'sliding-window',
  windowMs: 15 * 60 * 1000,
  max: 5,
});

// API限流:1分钟最多100次
const apiLimiter = new RateLimiter({
  algorithm: 'sliding-window',
  windowMs: 60 * 1000,
  max: 100,
});

核心特点

特点说明
精确度⭐⭐⭐⭐⭐ 任意连续60秒内严格≤100次
边界问题❌ 无边界问题,不会瞬时超频
并发支持✅ 支持100个并发请求
内存占用⚠️ 每个key约800字节(存储所有时间戳)
计算复杂度O(n) 需要过滤时间戳数组

工作原理

时间轴(每个请求都有独立的窗口):

0ms 检查:    窗口 = [-60000ms, 0ms]
100ms 检查:  窗口 = [-59900ms, 100ms]
1000ms 检查: 窗口 = [-59000ms, 1000ms]

每次检查都往前推60秒,动态计算窗口内的请求数
→ 不存在边界问题
→ 精确限制

优缺点

✅ 优点

  1. 最精确的限流控制
  2. 无窗口边界问题
  3. 公平性最好
  4. 推荐作为默认算法

❌ 缺点

  1. 内存占用较高(需要存储所有时间戳)
  2. 计算开销稍大(需要过滤数组)
  3. 不允许突发流量

2. 固定窗口 (Fixed Window)

适用场景

推荐

  • 高并发场景(具体吞吐需压测确认)
  • 对精度要求不高的场景
  • 内存受限的环境

不推荐

  • 需要精确限流的场景
  • 严格的安全控制(如登录保护)

配置示例

const { RateLimiter } = require('flex-rate-limit');

// 高并发API:1分钟最多100次
const highConcurrencyLimiter = new RateLimiter({
  algorithm: 'fixed-window',
  windowMs: 60 * 1000,
  max: 100,
});

核心特点

特点说明
精确度⭐⭐ 窗口内精确,边界不精确
边界问题⚠️ 窗口边界可能2倍超频(200次)
内存占用✅ 每个key仅8字节(只存计数器)
计算复杂度O(1) 最快

工作原理

时间轴(固定窗口):

0ms                      60000ms                    120000ms
│─────── 窗口0 ──────────│─────── 窗口1 ───────────│

窗口0: [0ms, 60000ms)    → count: 100
窗口1: [60000ms, 120000ms) → count: 100

边界问题:
59000ms-59999ms: 100次(窗口0)
60000ms-60999ms: 100次(窗口1)
→ 连续2秒内允许200次!⚠️

优缺点

✅ 优点

  1. 计算最快(O(1))
  2. 内存占用最低
  3. 实现简单

❌ 缺点

  1. 窗口边界有突发问题(可能2倍超频)
  2. 不符合"任意60秒"的语义
  3. 不公平(边界时刻的用户可以发更多请求)

3. 令牌桶 (Token Bucket)

适用场景

强烈推荐

  • API网关
  • 允许短时突发的场景
  • 批量操作(如批量导入)
  • 用户体验要求高的场景

不推荐

  • 不能接受突发流量的场景
  • 需要严格限制的场景(如登录)

配置示例

const { RateLimiter } = require('flex-rate-limit');

// API网关:允许突发100次,平均每秒100次
const gatewayLimiter = new RateLimiter({
  algorithm: 'token-bucket',
  capacity: 100,      // 桶容量(最大突发)
  refillRate: 100,    // 每秒补充100个令牌
  windowMs: 1000,     // 1秒
});

// 批量操作:允许突发50次,平均每秒10次
const batchLimiter = new RateLimiter({
  algorithm: 'token-bucket',
  capacity: 50,
  refillRate: 10,
  windowMs: 1000,
});

核心特点

特点说明
精确度⭐⭐⭐⭐ 平均速率精确
突发支持✅ 允许突发(桶容量)
内存占用✅ 每个key约16字节
计算复杂度O(1) 快速

工作原理

令牌桶模型:

┌────────────────────┐
│  桶(capacity=100) │
│  🪙🪙🪙🪙🪙🪙🪙      │  ← 令牌
└────────────────────┘
      ↑ 按速率补充
      ↓ 请求消耗

特性:
1. 桶满时可瞬间处理100个请求(突发)
2. 之后以refillRate速率补充令牌
3. 长期看严格限制平均速率

优缺点

✅ 优点

  1. 允许突发流量(用户体验好)
  2. 平均速率控制严格
  3. 内存占用低
  4. 适合API网关

❌ 缺点

  1. 可能瞬间消耗所有令牌
  2. 配置相对复杂(需要理解capacity和refillRate)
  3. 不适合严格限制的场景

4. 漏桶 (Leaky Bucket)

适用场景

强烈推荐

  • 流量整形(平滑流量)
  • 保护后端系统
  • 需要恒定输出速率的场景
  • 消息队列限流

不推荐

  • 允许突发的场景
  • 用户体验要求高的场景

配置示例

const { RateLimiter } = require('flex-rate-limit');

// 保护后端:恒定速率处理,每秒100个
const backendProtector = new RateLimiter({
  algorithm: 'leaky-bucket',
  capacity: 100,     // 桶容量
  leakRate: 100,     // 每秒漏出100个
  windowMs: 1000,
});

// 消息队列:平滑处理,每秒10个
const queueLimiter = new RateLimiter({
  algorithm: 'leaky-bucket',
  capacity: 50,
  leakRate: 10,
  windowMs: 1000,
});

核心特点

特点说明
精确度⭐⭐⭐⭐ 输出速率恒定
平滑性⭐⭐⭐⭐⭐ 最平滑
内存占用✅ 每个key约16字节
计算复杂度O(1) 快速

工作原理

漏桶模型:

┌────────────────────┐
│  桶(capacity=100) │
│  💧💧💧💧💧          │  ← 水(请求)瞬间进入
└─────────┬──────────┘
          ↓ 按时间自然漏出(释放额度)

关键:恒定速率 = leakRate / windowMs

配置示例:
leakRate: 100(每秒漏100个)
windowMs: 1000(1秒)

恒定速率计算:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
每秒漏100个 = 每10毫秒漏1个 = 每个请求间隔10ms

具体表现:
0ms:    容量内请求立即允许,桶水位上升
10ms:   水位自然下降约1个请求容量
20ms:   水位继续下降
...
1000ms: 水位基本清空

注意:当前库实现的是请求准入控制,不维护等待队列,也不会后台匀速派发请求。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

实际场景:
1. 瞬间发送100个请求 → 容量内请求立即允许 ✅
2. 后续请求按水位衰减速度重新获得额度
3. 约1000ms后水位清空

vs 令牌桶:
- 令牌桶:0ms时立即处理完100个 ⚡
- 漏桶:容量内立即允许,后续按漏出速率恢复额度 🐌

优缺点

✅ 优点

  1. 平滑流量(输出速率恒定)
  2. 削峰填谷
  3. 保护后端系统
  4. 内存占用低

❌ 缺点

  1. 不适合突发场景
  2. 用户体验稍差(感觉"慢")
  3. 配置相对复杂

5. 令牌桶 vs 漏桶

很多人容易混淆这两个算法,这里详细对比:

维度令牌桶漏桶
突发处理✅ 允许(立即处理)⚠️ 接受但平滑处理
处理方式请求驱动(有令牌就处理)速率驱动(恒定速率)
用户体验⭐⭐⭐⭐⭐ 好⭐⭐⭐ 一般
后端保护⭐⭐⭐ 一般⭐⭐⭐⭐⭐ 好

实际场景对比

场景:瞬间发送100个请求

令牌桶:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0ms: 100个请求 → 立即处理100个 ✅
用户感知:快速响应
后端压力:瞬间100个请求
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

漏桶:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0ms: 100个请求 → 放入桶 ✅
0-1000ms: 以恒定速率漏出(处理100个)
用户感知:稍慢(平滑处理)
后端压力:恒定速率(每秒100个)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

6. 选择决策树

开始

├─ 需要精确限流?
│  ├─ 是 → 滑动窗口 ⭐⭐⭐⭐⭐
│  └─ 否 ↓

├─ 允许短时突发?
│  ├─ 是 → 令牌桶 ⭐⭐⭐⭐
│  └─ 否 ↓

├─ 需要平滑流量?
│  ├─ 是 → 漏桶 ⭐⭐⭐⭐
│  └─ 否 ↓

└─ 追求极致性能?
   └─ 是 → 固定窗口 ⭐⭐⭐

7. 实战推荐配置

登录保护

// 推荐:滑动窗口(精确限制)
const loginLimiter = new RateLimiter({
  algorithm: 'sliding-window',
  windowMs: 15 * 60 * 1000,  // 15分钟
  max: 5,                     // 最多5次
});

API网关

// 推荐:令牌桶(允许突发)
const gatewayLimiter = new RateLimiter({
  algorithm: 'token-bucket',
  capacity: 100,
  refillRate: 100,
  windowMs: 1000,
});

消息队列

// 推荐:漏桶(平滑流量)
const queueLimiter = new RateLimiter({
  algorithm: 'leaky-bucket',
  capacity: 50,
  leakRate: 10,
  windowMs: 1000,
});

高并发API

// 推荐:固定窗口(性能优先)
const highQpsLimiter = new RateLimiter({
  algorithm: 'fixed-window',
  windowMs: 60 * 1000,
  max: 10000,
});

8. 性能对比维度(10000个用户)

以下是算法层面的相对特征,具体 QPS/延迟请以可复现 benchmark 或生产压测为准。

算法内存占用吞吐特征延迟特征
滑动窗口最高(保存窗口内请求时间)相对较低取决于窗口内记录数和存储后端
固定窗口最低(单计数器)通常最高最低
令牌桶低(桶状态)通常较高
漏桶低(桶状态)通常较高

9. 常见问题

Q1: 默认算法是什么?

A: 滑动窗口(sliding-window

滑动窗口是最精确、最公平的算法,推荐作为默认选择。

Q2: 什么时候用固定窗口?

A: 仅在以下情况:

  • 超高并发(具体吞吐需压测确认)
  • 内存非常受限
  • 可以接受窗口边界的2倍超频

Q3: 令牌桶和漏桶如何选择?

A:

  • 用户体验优先 → 令牌桶(允许突发)
  • 后端保护优先 → 漏桶(平滑流量)

Q4: 可以切换算法吗?

A: 可以,只需修改 algorithm 配置:

const limiter = new RateLimiter({
  algorithm: 'token-bucket',  // 修改这里
  // ... 其他配置
});

10. 相关文档


📚 相关文档

深入分析

应用配置

返回


最后更新: 2026-02-05
维护者: AI Assistant