想象一座大型游泳馆:十几支游泳队租用同一个泳池,管理员在门口立了一块牌子——“每支队伍2小时内最多下水200人次”。如果只有一支队伍,门口数人头就行;可当多支队伍同时抢泳道,有人超时赖着不走,有人悄悄借队友的入场券,还有人眼看额度快用完就疯狂加塞——此时,游泳馆需要的就不仅仅是一个计数器,而是一整套精确的“用量账本”:每支队伍有哪些额度、按什么维度组合统计、超过阈值怎么熔断、队伍之间能不能临时借票周转。这正是多租户LLM平台的配额管理(Quota)在做的事。 Token就像入场券,租户就像游泳队,LLM服务就像那片泳池。本文用“游泳馆闸机与入场券”这个比喻,带你拆解Quota的模型设计、多维度组合、计数引擎、滑动窗口、分级管控、超额熔断、限流联动与配额借用。你会发现,Azure 和 OpenAI 的配额体系之所以灵活又精确,靠的正是这套“泳池账本”逻辑。

岗位关联

配额管理是AI平台从“能用”走向“可运营”的分水岭。掌握它,你就能为企业级多租户LLM服务设计出既公平又高效的用量管控体系,直接支撑计费、容量规划与SLA承诺。

目录
  1. 可视化演示
  2. 配额模型设计
  3. 多维度组合
  4. 计数引擎架构
  5. 滑动窗口计数
  6. 分级管控策略
  7. 超额熔断机制
  8. 限流联动配置
  9. 配额交易借用
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

动态展示梯度下降算法在损失函数曲面上的优化轨迹,支持学习率调节与迭代控制,直观呈现收敛过程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

梯度下降轨迹 小球沿损失函数曲面滚动,每步沿负梯度方向更新 学习率控制步长大小,过大可能震荡发散 θ ← θ - η∇L(θ) 收敛至损失函数最小值点

配额模型设计

游泳馆管理员给每支游泳队发一张“入场配额卡”,卡片上有四栏:队名(哪支队伍)、池区(用哪个池)、时段(两个小时内有效)、总次数(最多200人次)。你一眼就能看清这个队伍“在什么时间、能游多少”。这张卡,就是配额模型的原型。

落到技术层面,配额(Quota)也是一个四元组。它不关心你每秒跑几次请求,只关心“这段时间内你总共能用掉多少Token”。在最朴素的形态里,Quota 由租户、模型、窗口和上限四个要素构成。租户指使用平台的一个组织或业务单元;模型指 GPT-4、Claude 等具体服务;窗口指统计周期,如 1 小时、1 天;上限则是这个窗口内允许消耗的 Token 总量。

回到游泳馆的比喻:管理员不需要记住每支队伍每一次入场,只需要维护好这张“配额卡”。当闸机扫码时,系统自动比对“当前窗口内已用次数 + 本次入场 ≤ 卡片上限”,决定放行还是拦截。多租户平台的配额模型,本质上就是这张电子化的泳池配额卡。

Q=(G, M, W, L)Q = (G,\, M,\, W,\, L)
这意味着:一条配额 = 谁(租户)+ 用什么(模型)+ 多久(窗口)+ 用多少(上限)。
  1. 配额实体Q 代表一条完整的配额记录,是账本上的一行。
    QQ
  2. 租户维度G(Group)指配额归属的租户或用户组,就像游泳队名。
    GG
  3. 模型维度M(Model)指被调用的 LLM 服务,对应游泳馆里的泳池区。
    MM
  4. 窗口与限额W 是统计周期长度,L 是该周期内的 Token 上限。
    (W, L)(W,\, L)

多维度组合

现实中的游泳馆不会只发一张总卡。管理员发现,如果只看总量,早上八点的儿童培训班可能把泳池挤爆,导致下午的专业队无池可用。于是限额被拆成多个维度:每个队伍每小时最多 30 人、儿童池最多 10 人、深水区最多 15 人。任何一维超了都不放行。这就是“多维度组合”。

LLM 平台的用量同样要按维度切分。租户可能同时拥有多个项目、调用多个模型、从 Web 端和 API 端发起请求。配额系统需要支持按时间、空间(项目/群组)、请求类型(模型/接口)分别设限。当一条请求到来时,系统计算它在每个维度上的当前消耗,只要任一维度越过阈值,就拒绝放行。

配合游泳馆的场景理解:就算队伍总次数还剩很多,只要儿童池名额用完,儿童池闸机依旧关闭。对 LLM 平台来说,这意味着“项目总额度还有”不等于“某个模型还能调用”。维度组合的价值,是让配额从“一笔糊涂账”变成“分门别类的精细账”。

Qtotal=min⁡(Qtime, Qspace, Qmodel)Q_{\text{total}} = \min\big(Q_{\text{time}},\, Q_{\text{space}},\, Q_{\text{model}}\big)
有效配额 = 时间维度限、空间维度限、模型维度限三者的最小值——任何一条先碰顶,整体就不能再进。
  1. 时间维度约束Q_time 是“当前窗口内已用 Token 数”对应的剩余上限。
    QtimeQ_{\text{time}}
  2. 空间维度约束Q_space 是“当前项目/租户组”维度的配额上限。
    QspaceQ_{\text{space}}
  3. 模型维度约束Q_model 是“当前模型接口”维度的配额上限。
    QmodelQ_{\text{model}}
  4. 取最小值总有效配额 = min(三者),任何一维耗尽即视为整体配额耗尽。
    min⁡(⋅)\min(\cdot)

计数引擎架构

闸机计数看似简单,但在游泳馆高峰时段,几十个入口同时有人通过,每过一人计数器都要加一。如果计数过程慢了,闸杆就会卡顿,后面排队的人会抱怨。更危险的是,两个闸机同时读到同一个数字,各自加一写回,就漏算了人数。计数引擎的架构决定了这一切是否可靠。

LLM 平台的计数引擎有三个主流方案:纯内存计数、Redis 原子计数、异步批量落账。纯内存最快,但进程重启就丢数据,适合单机场景;Redis 的 INCR 提供原子自增,多台网关同时计数也不会重复或丢失,是分布式场景的标配;异步批量落账则是把计数先攒在内存队列,定期刷入 Redis,用牺牲一点实时性换吞吐量。实际生产环境常采用三层混合:内存做快速路径,Redis 做统一账本,批量任务做最终一致。

游泳馆的角度看,Redis INCR 就像每个入口的独立电子闸机——每个闸机不必互相协调,因为闸机内部芯片保证“过一人、计一下”不会算错。只要所有闸机的数据都汇总到同一本账,管理员就永远知道每支队伍已经用了多少配额。

C←INCR(key),Δ=O(1)C \leftarrow \mathrm{INCR}(key),\quad \Delta = O(1)
Redis 的 INCR 是原子操作,多条并发请求同时自增也不会互相覆盖,且每次自增耗时恒定。
  1. 原子自增INCR 让计数器执行“读-加-写”的原子操作,并发下不会丢计数。
    INCR(key)\mathrm{INCR}(key)
  2. 计数键key 是区分维度的唯一标识,如“租户A:模型B:2025-01-01:窗口C”。
    keykey
  3. 常数时间复杂度单次计数耗时 O(1),不随历史数据量增长而变慢。
    O(1)O(1)

滑动窗口计数

管理员很快发现一个新问题:如果按“整点重置”计数,游泳队在 12:59 冲进 30 人,13:00 又可以再进 30 人,一分钟内涌进 60 人,泳池瞬间爆满。问题的根源是固定窗口在边界上的“重置效应”。游泳馆需要的是滚动着看的“过去 1 小时”,而不是每整点清零的“本小时”。

滑动窗口的做法是:把时间切成小片(比如 1 分钟一个片),窗口 = 最近 60 个片。每一分钟,最旧的一片滑出窗口,最新的一片滑入。当前时刻 t 的用量 U(t) 就是窗口内所有片的累计值。这样即使跨越整点边界,也不会出现“12:59 的 30 人 + 13:00 的 30 人同时被计入同一小时”的突刺。

游泳馆的场景里,滑动窗口相当于一个“移动的 1 小时沙漏”:沙漏随真实时间不断旋转,永远只统计最近 1 小时入场的总人数。12:59 进的那批人,到 13:59 自然滑出窗口,不再计入用量——精准、平滑、无突刺。

U(t)=∑i=t−W+1tCiU(t) = \sum_{i = t-W+1}^{t} C_i
U(t) 表示当前时刻往前推 W 个时间片的用量总和,每过一片,窗口整体向后滑动一格。
  1. 窗口起点i = t-W+1 是当前滑动窗口中最旧的时间片编号。
    i=t−W+1i = t-W+1
  2. 窗口终点t 是当前时刻对应的时间片,窗口实时滚动到最新位置。
    tt
  3. 求和运算把窗口内每个时间片的请求量 C_i 累加,得到窗口总用量 U(t)。
    ∑iCi\sum_{i} C_i

分级管控策略

游泳馆不会等到队伍超员才行动。经验丰富的管理员会在入场数达到限额的 70% 时提醒队长,达到 90% 时放缓放行速度,达到 100% 时才彻底关闸。分三级递进,既给租户留出缓冲,也保护平台不被击穿。

LLM 配额同样需要分级管控。第一级是预警:用量达到阈值的 β₁ 倍时,系统给租户发送告警,并允许请求继续。第二级是限速:达到 β₂ 倍时开始降低请求的放行速率,但业务仍可运转。第三级是熔断:达到硬性上限时拒绝新请求。每个级别对应不同的动作,从“提醒”到“降速”再到“拒绝”,形成一个平滑的治理阶梯。

把三级管控想象成裁判的三张牌:黄牌(预警)提醒你注意动作,红牌前先出示警告(限速),红牌(熔断)才真正罚出场。这套策略让配额管理从“非黑即白”变成“有梯度、可协商”,大幅降低误伤正常业务的风险。

Level={0U<β1L1β1L≤U<β2L2β2L≤U<L3U≥L\text{Level} = \begin{cases} 0 & U < \beta_1 L \\ 1 & \beta_1 L \le U < \beta_2 L \\ 2 & \beta_2 L \le U < L \\ 3 & U \ge L \end{cases}
其中 β₁ 通常取 0.7,β₂ 通常取 0.9,L 是硬上限;级别随用量逐级上升,动作也逐级加剧。
  1. 正常档(0 级)用量低于预警阈值 β₁L,请求全部放行。
    U<β1LU < \beta_1 L
  2. 预警档(1 级)用量在 β₁L 与 β₂L 之间,通知租户提前收缩。
    β1L≤U<β2L\beta_1 L \le U < \beta_2 L
  3. 限速档(2 级)用量接近上限,开始降低访问速率。
    β2L≤U<L\beta_2 L \le U < L
  4. 熔断档(3 级)用量达到硬上限 L,拒绝新请求。
    U≥LU \ge L

超额熔断机制

熔断机制是所有配额系统的最后一道保险。当滑动窗口的累计用量达到硬性上限时,闸机必须果断放下闸杆,阻止更多请求进入,否则平台会因为个别租户的超额消耗而影响其他租户。

熔断触发后,系统进入拒绝状态:所有耗用 Token 的新请求直接返回 429 或配额不足错误。但熔断不应是永久性的。冷却期(Cooldown)结束后,系统再次检测窗口用量,若已滑出超额部分(比如旧用量过了窗口期),则自动恢复放行。这就像一个保险丝:电流过大熔断,排查修复后重新推上闸刀,电路恢复正常。

游泳馆的闸机在人数满员时“咔”地落下,但这不意味着今天再也不开放——等一批人离开,闸机重新抬起,新的一批才能进入。多租户平台的熔断机制同样讲究“快速恢复”:熔断后持续监控窗口用量,一旦超额部分滑出窗口,配额自动回充,服务即可恢复。

Accept(r)={rejectU(t)≥Lhardallowotherwise\text{Accept}(r) = \begin{cases} \text{reject} & U(t) \ge L_{\text{hard}} \\ \text{allow} & \text{otherwise} \end{cases}
一个请求能否被接受,取决于当前窗口用量 U(t) 是否触碰硬上限 L_hard。
  1. 触发条件当前窗口累计用量 U(t) 达到或超过硬上限 L_hard 时启动熔断。
    U(t)≥LhardU(t) \ge L_{\text{hard}}
  2. 拒绝路径熔断期间新请求被拒,避免继续超支。
    reject\text{reject}
  3. 放行路径用量未达上限时正常放行,系统保持健康。
    allow\text{allow}

限流联动配置

很多初学者分不清限流(Rate Limiting)和配额(Quota)的区别。游泳馆里,限流像“闸机每秒钟最多放 3 个人通过”,配额像“每支队伍 2 小时最多 200 人”。限流管瞬时速率,配额管窗口总量。真正成熟的系统会把两者联动起来。

联动的核心是“双条件同时满足才放行”:一方面请求速率不能超过令牌桶允许的瞬时值 R_max;另一方面,窗口剩余配额还够用。如果配额余额只剩最后 10 个 Token,即使闸机毫秒级放行,也应该按剩余量动态调整允许速率。这样既避免了瞬时涌塞,又保证了总额度不会被突破。

游泳馆管理员把闸机速度和总入场量联动:高峰时段即使人不多,也会因为“本小时剩余名额少”而自动放缓放行。这样限流保证了系统的瞬时安全,配额保证了长期公平,两者配合,水位始终可控。

Rallow=min⁡(Rmax, Lhard−U(t)Wremain)R_{\text{allow}} = \min\Big(R_{\text{max}},\, \frac{L_{\text{hard}} - U(t)}{W_{\text{remain}}}\Big)
允许速率 = min(瞬时上限,配额剩余量 ÷ 窗口剩余时间)。配额越少,放行越慢。
  1. 瞬时速率上限R_max 是令牌桶层允许的最大请求速率,防止短时并发冲垮服务。
    RmaxR_{\text{max}}
  2. 配额剩余量L_hard - U(t) 是当前窗口还剩下的可用 Token 数。
    Lhard−U(t)L_{\text{hard}} - U(t)
  3. 窗口剩余时间W_remain 是当前窗口到结束的剩余时长。
    WremainW_{\text{remain}}
  4. 取最小值两个条件任一更紧,就以它为准,实现双重约束。
    min⁡(⋅)\min(\cdot)

配额交易借用

游泳馆的旺季,A 队临时加训需要多 50 张入场券,B 队下午休息正好用不完。管理员的做法是:让 B 队把多余的额度挂到公共配额池,A 队从池中借用 50 张,约定一周后归还。这就是配额交易与借用机制。

技术实现上,平台会维护一个公共配额池(Quota Pool)。每个租户的净可用配额 = 基础配额 + 借入配额 − 借出配额。借用操作需要确认出借方的剩余额度充足,并写入台账;归还可以是“立即归还”或“按下一窗口额度抵扣”;所有借出与借入都会记录在计量结算系统中,作为月底对账和成本分摊的依据。这样既充分利用了闲置配额,又保证了每笔账目可追溯。

回到游泳馆,借票必须留下凭证:谁借的、借了多少、什么时候还。配额借用也一样,借出方在自己额度紧张时可以收回,借入方在窗口期结束后自动归还。这套“有借有还、再借不难”的机制,让平台的配额利用率大大提升,也让租户之间形成了良性的资源共享生态。

Qnet=Qbase+Qborrow−QlendQ_{\text{net}} = Q_{\text{base}} + Q_{\text{borrow}} - Q_{\text{lend}}
租户的净配额 = 基础配额 + 借入的配额 − 借出的配额。正向为多可用,负向表示欠账。
  1. 基础配额Q_base 是平台分配给租户的初始额度,相当于队伍的年卡数量。
    QbaseQ_{\text{base}}
  2. 借入配额Q_borrow 是从公共配额池或其他租户借来的额外额度。
    QborrowQ_{\text{borrow}}
  3. 借出配额Q_lend 是当前借给其他租户的额度,借出期间自己不可用。
    QlendQ_{\text{lend}}
  4. 净配额Q_net 是租户当前真正可用的净额度,结算和熔断都以它为准。
    QnetQ_{\text{net}}

AI 应用场景

SaaS 平台的每租户 LLM 用量账本

阶段:部署运维与成本治理

解决的问题:大型 SaaS 平台同时服务数百家企业客户,每个客户调用 LLM 接口的 Token 消耗天差地别。传统做法是“一刀切”限制所有客户,要么大客户频繁撞墙,要么小客户肆意刷量造成成本失控。AI 方案是用 Quota 系统为每个租户配置独立的配额模型,按项目、模型、时间窗口多维度组合计数,超过分级阈值自动预警或熔断,实现精细化的成本分摊和用量控管。

具体案例:某办公协作 SaaS 的 AI 助手功能,接入了 GPT-4 和开源模型两条通道。平台为每个企业客户配置 每日 500 万 Token 基础配额,并允许客户在后台申请临时扩额。某客户在月底结算前集中导出文档,触发 90% 预警阈值后系统自动降速,避免了超额费用;而另一家大客户的 3D 渲染团队通过借用市场部门的闲置配额,顺利度过了上线高峰期。

企业内部 AI 网关的配额共享池

阶段:模型网关运维与容量调度

解决的问题:大型企业内部有多个业务部门共享统一的 LLM 网关,部门之间用量峰谷差异明显。传统方式给每个部门固定配额,导致“闲的闲死、忙的忙死”。AI 方案通过配额池化 + 借用机制,让各部门在额度紧张时自动向池中借用,池中余量不足时才按优先级分配,最终实现全体部门在总预算约束下的动态均衡。

具体案例:某金融集团的数据分析部和客服机器人部共用一套 LLM 网关。月末财报季,数据分析部需批量处理文档,客服部夜间咨询量骤降。配额系统检测到客服部窗口用量仅为 30%,自动将其闲置配额放入公共池;数据分析部实时借用后顺利完成处理,月底结算时客服部收到“配额出借收益”抵扣账单,双方共赢。

Python 演示

python63 行
import timefrom collections import dequefrom threading import Lock  class SlidingWindowQuota:    """基于滑动窗口的配额管理器,对应游泳馆闸机计数"""     def __init__(self, tenant, limit, window_sec=60):        self.tenant = tenant        self.limit = limit        self.window_sec = window_sec        self._records = deque()        self._lock = Lock()        self._borrow_amount = 0        self._lend_amount = 0     def _effective_limit(self):        return self.limit + self._borrow_amount - self._lend_amount     def _trim(self, now):        while self._records and now - self._records[0][0] > self.window_sec:            self._records.popleft()     def can_consume(self, cost=1):        with self._lock:            now = time.time()            self._trim(now)            used = sum(v for _, v in self._records)            return used + cost <= self._effective_limit()     def consume(self, cost=1):        with self._lock:            now = time.time()            self._trim(now)            used = sum(v for _, v in self._records)            if used + cost > self._effective_limit():                return False            self._records.append((now, cost))            return True     def borrow(self, amount):        with self._lock:            self._borrow_amount += amount     def repay(self, amount):        with self._lock:            self._borrow_amount = max(0, self._borrow_amount - amount)  def main():    q = SlidingWindowQuota("team-a", limit=100, window_sec=60)    print("消耗20:", q.consume(20))    print("剩余判断:", q.can_consume(90))    q.borrow(60)    print("借用60后再判断:", q.can_consume(90))    print("消耗90:", q.consume(90))    q.repay(30)    print("归还30后剩余判断:", q.can_consume(60))  if __name__ == "__main__":    main()
目的:用 Python 实现一个支持滑动窗口、超额判断和配额借用的多租户配额管理器,演示算法如何工作。
  1. self._records = deque()用双端队列存储每个请求的时间戳和 Token 数,左侧过期数据随时弹出。
  2. def _trim(self, now)核心清理逻辑:队首记录若超出窗口时长则弹出,保证窗口内永远只有“最近 W 秒”的用量。
  3. used = sum(v for _, v in self._records)把窗口内所有请求的 Token 数累加,得到当前窗口已用量 U(t)。
  4. used + cost <= self._effective_limit()判断“已有用量 + 本次请求”是否不超过净配额;超过则熔断拒绝。
  5. self._borrow_amount += amount借用配额直接提升净配额上限,模拟从公共配额池借票。
  6. with self._lock:加锁保证多线程并发计数时不出现竞态,模拟 Redis INCR 的原子性。

常见误区

误区“Quota 不就是每秒最多处理多少请求吗?我只要在网关上加个限流器,把 QPS 限制住,就完成了配额管理。”

正解两者的观察维度根本不同。限流(Rate Limiting)管的是“每秒最多放多少个请求进来”,像一个闸机每秒钟只放 3 个人;配额(Quota)管的是“这段时间里总共还能用掉多少 Token”,像一个窗口期泳池最多容纳 200 人次。限流解决瞬时冲击,配额解决总量失控。即便 QPS 恒定,一个租户持续跑满 24 小时也会把月配额耗尽,所以必须同时有配额账本。

为何常见:初学者接触的第一个防护手段往往就是限流器,容易产生“限流可以覆盖所有场景”的错觉,忽略了限流无法统计长期累计消耗的本质。

误区“把 1 分钟窗口换成 5 分钟窗口,再在整点重置,效果就和滑动窗口差不多了。”

正解固定窗口无论怎么调大小,都有边界重置效应。假设每分钟限 100:12:59 用掉 100,13:00 又立刻放行 100,两秒内涌进 200,这就是“边界突刺”。滑动窗口通过按小时间片滚动累加,任何时刻看到的总量都是“最近一个完整窗口期”的用量,从数学结构上消除了边界重置的破洞。窗口大小影响的是精度和平滑度,不是有没有破洞。

为何常见:很多教程先讲固定窗口再讲滑动窗口,学习者误以为后者只是前者的改进版,却没有理解两者在窗口边界处理上的本质差异。

误区“配额达到 100% 了,直接返回 429,所有请求全部打回,这样最安全。”

正解分级管控的意义正在于此。熔断是所有手段里最后一道,不应是唯一手段。合理策略是:70% 预警、90% 限速、100% 熔断。而且熔断期间,像“获取配额剩余量”“查询用量”这类零成本请求仍应放行,否则租户连自己为什么被熔断都查不到。硬拒绝只针对真正消耗 Token 的请求,不能把管理层入口也堵死。

为何常见:教科书式的“超过上限就拒绝”让人形成非黑即白的判断,忽视了生产系统中“熔断的目的是保护而非惩罚”这一核心思想。

误区“Redis 毕竟是数据库,计数偶尔丢一次两次不影响大局,没必要纠结原子性。”

正解配额账本差一个 Token 就是一笔算不清的账。如果网关先 GET 再 SET,两个并发请求会互相覆盖导致漏计;即使用 INCR,如果计数键和过期键分开设置,进程崩溃时也可能出现“计数存入但过期没设上”的孤儿键。生产级做法是用 Lua 脚本把 INCR 和 EXPIRE 在一个原子操作中完成,或者使用 Redis 事务,确保计数与过期永远一致。

为何常见:很多人在本地环境用单机 Redis 测试,并发量小看不出竞态问题,便低估了高并发下读写不一致发生的概率。

误区“A 队额度多,B 队额度少,B 直接从 A 的额度里扣就行了,记不记账无所谓。”

正解借用不是“偷用”。如果 B 借走 A 的 50 Token 不记账,A 的用量数据就会凭空变多,导致 A 提前触发熔断,而且月底对账时无从追溯。真正的借用机制必须包含三方记录:借出方扣减、借入方增加、中央台账留痕。归还时同样要入账,结算系统才能算出每个租户实际消费和净借还款。这就像图书馆借书,不扫码不登记,书就再也找不回来。

为何常见:借用场景常出现在同事间“互相帮忙”的轻松氛围里,让人忽略了它本质上是一笔跨租户的资金流转,必须纳入正式计量体系。

面试题

Q1. Quota 配额管理关注的核心指标是?

单选题 · easy

  • A.窗口期内的 Token 总量← 正确答案
  • B.每秒请求数的峰值
  • C.模型推理的延迟
  • D.训练样本的数量

答案:A。配额管的是“一段时间内能用掉多少 Token”,而不是瞬时速率或延迟。就像游泳馆管的是“2 小时内最多 200 人次”,而不是“每秒过几个人”。

Q2. 配额四元组 Q=(G,M,W,L) 中 L 表示?

单选题 · mid

  • A.租户标识
  • B.模型名称
  • C.窗口时长
  • D.配额上限← 正确答案

答案:D。L 是 Limit,即窗口内的 Token 上限。G 是租户组,M 是模型,W 是窗口时长。四个维度各司其职,L 负责“上限”约束。

Q3. 固定窗口在边界处可能导致的经典问题是?

单选题 · mid

  • A.数据永久丢失
  • B.窗口突刺流量← 正确答案
  • C.计数无上限
  • D.模型无法加载

答案:B。固定窗口每到一个整点就重置,12:59 已用满 100 的租户在 13:00 又能立刻用 100,造成边界突刺。滑动窗口通过滚动统计消除了这个破洞。

Q4. 某租户固定窗口 12:00-13:00 已用 90 Token,下一窗口 13:00-14:00 重置为 100。12:59 到 13:01 期间两窗口都被利用,1 分钟内最多消耗多少 Token?

单选题 · hard

  • A.90 Token
  • B.100 Token
  • C.190 Token← 正确答案
  • D.200 Token

答案:C。12:59 属于第一个窗口,已用 90 可再补 10;13:00 进入新窗口,又可用满 100。边界处 1 分钟内最多 = 90 + 100 = 190。这正是固定窗口边界突刺的数学体现。

Q5. Redis 的 INCR 命令用于配额计数,它的核心特性是?

单选题 · easy

  • A.原子自增← 正确答案
  • B.自动回滚
  • C.异步写入
  • D.随机采样

答案:A。INCR 是原子操作,“读-加-写”不可分割,多个并发请求同时执行也不会互相覆盖。这是多网关分布式计数的基础保障。

Q6. 滑动窗口中,当前时刻 t 的用量 U(t) 等于?

单选题 · mid

  • A.今天累计全部用量
  • B.过去 W 个时间片用量之和← 正确答案
  • C.当前请求的单次用量
  • D.未使用配额的总额

答案:B。滑动窗口把时间切成片,U(t) = 从 t-W+1 到 t 共 W 个时间片的用量累加。它永远只统计“最近一个窗口期”,旧片自动滑出。

Q7. 配额的分级管控通常包含哪些层级动作?

多选题 · easy

  • A.预警提醒← 正确答案
  • B.限速降级← 正确答案
  • C.硬性熔断← 正确答案
  • D.模型参数调优

答案:ABC。分级管控的三步曲是预警、限速、熔断。模型参数调优属于训练环节,不是配额管控动作,故排除。

Q8. 配额借用机制通常需要哪些环节?

多选题 · mid

  • A.确认出借方余量← 正确答案
  • B.借出与借入记账← 正确答案
  • C.按时归还结算← 正确答案
  • D.重启网关服务

答案:ABC。借用必须先确认出借方有富余额度,再在双方账本和中央台账中记录,最后按约归还并结算。重启网关与借用逻辑无关。

Q9. 限流与配额联动时,允许速率 R_allow 通常需要考虑哪些因素?

多选题 · hard

  • A.瞬时最大速率← 正确答案
  • B.配额剩余余额← 正确答案
  • C.窗口剩余时间← 正确答案
  • D.模型训练轮数

答案:ABC。联动公式 R_allow = min(R_max, 配额剩余量 ÷ 窗口剩余时间)。瞬时速率上限、剩余余额、剩余时间三者共同决定放行速度,训练轮数不在此列。

Q10. 网关并发 100 个请求,此刻窗口已用 990/1000 Token,若系统先读取余额再在请求结束后统一 INCR,会发生什么?

单选题 · extreme

  • A.所有请求都被正确拒绝
  • B.多个请求同时读到 10 并发放行← 正确答案
  • C.配额永远无法耗尽
  • D.数据自动写入磁盘

答案:B。先读后写的非原子操作存在竞态:100 个并发请求可能都读到“剩余 10”,于是全部放行,最终用量变成 1090,超额 90。正确做法是先用 INCR 原子自增,再根据返回值判断是否允许,避免“读-判-写”的竞态窗口。