2. 令牌桶算法原理

令牌桶(Token Bucket)是限流与流量整形领域最经典的算法之一,也是 Redis 令牌桶实现的理论基础。理解其核心参数与数学模型,是后续编写 Lua 脚本与熔断策略的前提。

2.1 核心参数

参数 符号 说明 典型值(示例)
令牌生成速率 r 每秒钟向桶内放入的令牌数量,决定长期平均请求速率 100 个/秒
桶容量 b 桶最多能存放的令牌数,决定短时突发上限 200 个
当前令牌数 c 桶内实时可用令牌数量,随时间动态变化 0 ~ b
最后填充时间 t_last 上次更新令牌数的时间戳,用于计算增量 Unix 毫秒
关键认知:令牌生成速率 r 控制平均流量,桶容量 b 控制突发流量。两者独立配置,共同决定系统的限流行为。

2.2 令牌生成速率与桶容量

令牌桶的核心思想是:系统以恒定速率 r 向桶中注入令牌,桶最多容纳 b 个令牌。当请求到达时,必须从桶中消耗至少 1 个令牌才能被放行;如果桶为空,请求被拒绝或排队。

  • 令牌生成速率(r):决定了系统的稳态吞吐量。例如 r=100,表示长期平均每秒最多处理 100 个请求。
  • 桶容量(b):决定了系统允许的瞬时突发大小。例如 b=200,意味着在桶满的情况下,可以一次性处理 200 个请求,而不受 r 的限制。
设计原则:通常设置 b > r,使系统能应对短时流量尖峰。例如 r=100、b=200,允许 2 秒的突发流量。

2.3 突发流量处理

令牌桶相比漏桶算法的最大优势在于:允许突发。当桶内积累了大量令牌(最多 b 个),系统可以在短时间内以远高于 r 的速率处理请求,直到令牌耗尽。

突发处理流程:

  1. 空闲期:请求较少,令牌持续积累,桶逐渐变满。
  2. 突发到达:大量请求瞬间涌入,桶内令牌充足,所有请求立即被放行。
  3. 令牌耗尽:桶变空后,后续请求必须等待新令牌生成(受 r 限制),速率回落到 r。
注意:突发能力并非无限。最大突发请求数 = min(突发请求量, b)。如果突发请求量超过 b,超出的部分会被直接拒绝。

2.4 算法数学模型

令牌桶的数学模型基于时间差计算,避免使用定时器,适合在 Redis 中高效实现。

令牌补充公式:

// 当前时间 now,上次填充时间 t_last
// 时间间隔 delta = now - t_last(单位:秒)
// 应补充令牌数 = delta * r
// 新令牌数 = min(当前令牌数 + delta * r, b)

c_new = min(c + (now - t_last) * r, b)

请求消耗公式:

// 请求需要 n 个令牌(通常 n=1)
if c_new >= n:
    放行请求
    c_new = c_new - n
else:
    拒绝请求
    c_new 不变

完整单次请求算法(伪代码):

function allow_request(key, r, b, n=1):
    now = current_time_ms()
    c, t_last = redis.hget(key, "tokens", "last_time")

    // 1. 补充令牌
    delta = (now - t_last) / 1000.0
    c = min(c + delta * r, b)

    // 2. 判断是否放行
    if c >= n:
        c = c - n
        redis.hset(key, "tokens", c, "last_time", now)
        return true
    else:
        redis.hset(key, "tokens", c, "last_time", now)
        return false
数学本质:令牌桶是一个带容量限制的积分器。生成速率 r 是积分系数,桶容量 b 是积分上限,请求消耗是负反馈。该模型保证了长期平均速率 ≤ r,同时允许短时速率 > r。

2.5 令牌桶状态变化图

下图展示了令牌桶在空闲、突发、限流三个阶段的状态变化:

令牌桶状态变化示意图 阶段1:空闲积累 请求少,令牌持续生成 桶内令牌数逐渐增加 c → b(桶满) 速率 < r 时间 阶段2:突发处理 大量请求瞬间到达 消耗桶内积累令牌 c 快速下降 速率 > r(突发) 时间 阶段3:限流 令牌耗尽 c = 0 请求被拒绝 速率 = 0 说明: 令牌桶通过积累令牌实现突发处理,但突发能力受桶容量 b 限制。当桶空时,系统严格按速率 r 放行请求。 参数对比: 令牌生成速率 r = 100 个/秒 | 桶容量 b = 200 个 | 最大突发请求数 = 200 个 | 稳态速率 = 100 请求/秒

2.6 令牌桶 vs 漏桶对比

对比维度 令牌桶 漏桶
突发处理 支持,桶容量决定突发上限 不支持,强制平滑输出
长期平均速率 严格 ≤ r 严格 = r
实现复杂度 中等,需维护时间戳 较低,队列模型
适用场景 允许短时突发的业务(API 网关、秒杀) 要求绝对平滑的场景(视频流、数据同步)
Redis 实现 推荐,Lua 脚本高效 不推荐,队列操作复杂
选型建议:在微服务熔断与限流场景中,令牌桶是更优选择。它既能保护下游不被持续过载,又能容忍业务侧的合理突发。