周一早高峰,市中心地铁站迎来一天中最严峻的考验。A、B、C 三个入口同时涌进乌泱泱的人群,如果所有人毫无节奏地扑向闸机,站台会瞬间饱和,列车延误,甚至发生踩踏。地铁站的管理员是怎么做的?他们在安检口拉起了波浪护栏,让队伍一拨一拨地流动;闸机一次只放一个人;站台入口的电子屏实时显示站内人数;多个入口之间共享同一条客流规则。这场面,就是一套教科书式的分布式限流系统——乘客是请求,闸机是限流器,总控室里的共享台账就是 Redis 计数器。这篇文章不堆术语、不摆深奥理论,就带你把“分布式计数与限流”这出大戏,一站一站地看明白。学完之后你会发现:所谓高并发防护,不过是给系统装上一套聪明的“地铁闸机”。
限流是系统的“最后一道闸门”。掌握分布式计数与限流,你就能在流量洪峰下守住系统可用性,从被动救火变成主动设防——这是高并发系统架构师的必备基本功。
目录
可视化演示
展示限流三模式:令牌桶(令牌补充/消费/通过拒绝)、窗口对比(固定窗口临界突发vs滑动窗口平滑计数)、分布式计数(Redis INCR单点原子/G-Counter CRDT多副本合并)。
本节配有交互动效(进入视口后自动加载,约 3KB)。
令牌桶限流算法 令牌以固定速率补充到桶中,桶满则丢弃多余令牌 请求到达时检查令牌:有则消费通过,无则拒绝 tokens = min(capacity, tokens + rate*dt) 补充80/s,通过率约75%
1. 高并发之痛:流量洪峰下的崩溃危机
地铁站最怕什么?怕所有人同时挤向同一个闸机口。没有分流护栏、没有分批放行,几百人怼在一起,闸机死机、旋杆折断、站台爆满,整个车站陷入瘫痪。互联网系统也一样:秒杀活动开始的瞬间,几十万请求像洪水一样涌向服务器,数据库连接池被瞬间占满,线程池耗尽,CPU 飙到 100%,新来的请求只能排队等待,超时后又变成更沉重的重试风暴——这就是流量洪峰之下的系统雪崩。
我们常用“可用性”来衡量系统扛住了多少冲击。它描述的是系统在统计周期内正常工作的时间占比。同时,整个集群的能力受限于最弱的一环,也就是“木桶效应”:CPU、内存、数据库连接、带宽,任何一个资源到达瓶颈,都会拖垮全局。限流要解决的,正是如何在资源到达瓶颈之前,把流量控制在一个系统能承受的范围。
1. 正常运行时间:统计周期内系统保持可用的累计时长,用 UT 表示。
2. 宕机时间:系统不可用时间的总和,包括崩溃、宕机、重启等,用 DT 表示。
3. 总时间:正常与不可用时间之和,构成整个统计周期。
4. 计算可用性:用正常时间除以总时间再乘以 100%,得到可用性百分比。
为什么要在意可用性?每提高一个“九”,意味着一年内允许宕机的时间从 8.76 小时(99.9%)降到 52.6 分钟(99.99%)。对地铁站来说,一条线路瘫痪 52 分钟就是大事故;对互联网公司来说,这意味着真金白银的订单流失。所以,必须在流量到达系统之前就为它设好闸、限好速。
2. Redis原子计数:总控室的电子看板
地铁站总控室里挂着一块电子看板,写着“当前站内人数:128”。每放行一个乘客,安检员按一下“+1“;每出去一个,按一下”-1“。看起来很简单,可如果几个安检员同时冲到看板前,都看到 128,都按下”+1”,看板会变成多少?理想情况是 131,可实际完全可能停留在 129——因为两次更新都基于同一个旧值 128,后写的人把先写的人覆盖了。少记了两个人,可就是安全隐患。
这就是分布式计数的核心难点:并发写冲突。在分布式系统里,几十个实例可能同时在更新同一个计数器,必须保证更新操作的“原子性”——就像看板按钮被设计成机械锁定,一个人按下时,其他人的按钮物理按不动。Redis 的 INCR 命令正是这样的“机械锁定按钮”:把读旧值、加一、写回三步捆绑成一个不可分割的整体,任何时刻只允许一个客户端完整执行。
1. 旧计数值:Redis 中当前保存的数字,是本次操作的起点。
2. 加一运算:在旧值基础上执行加 1,得到中间结果。
3. 原子写回:加一后的新值立即覆盖旧值,整个过程不被其他命令打断。
有了这块电子看板,地铁站不仅能统计站内人数,还能顺手做很多事:给每个乘客发一个“当日进站序号”,这就是分布式的发号器;把每个入口的放行人数加在一起,这就是最原始的窗口计数。计数稳稳当当,限流的大楼才有了地基。
3. 固定窗口限流:五分钟一波的人流管制
有了电子看板,地铁站开始定规矩:以 5 分钟为一个“窗口”,这个窗口内最多放行 500 人。每进来一个人,看板 +1;一旦到了 500,闸机立即锁死;5 分钟结束,看板清零,重新放 500 人。简单、直观、极其好用——这就是固定窗口限流,它是所有限流算法里最容易理解的一种。
判断逻辑也非常直白:窗口内计数小于阈值就放行,大于等于就拒绝。它不需要记录每个请求的精确时间,只需要一个不断累加的计数器和一个到点清零的定时器,所以在 Redis 里实现起来成本极低。
1. 当前计数 count:本窗口内已经放行的请求数量。
2. 阈值 limit:管理者预先设定的窗口放行上限。
3. 未达阈值:count 小于 limit 时,请求被放行。
4. 达到阈值:count 达到或超过 limit 时,请求被拒绝。
固定窗口看起来完美,却藏着一个致命漏洞:边界突刺。假设 9:59:59 的窗口已有 499 人,最后一秒又放进 1 人;10:00:00 新窗口开始,又有 500 个新名额。从 9:59:59 到 10:00:01,短短两秒内最多可能涌进 1000 人,比平时多了一倍。地铁站如果这样设计,站台在窗口交界处依然会瞬间爆满——“固定的时间块”本身就是漏洞的来源。
4. 滑动窗口限流:滚动统计堵住边界漏洞
为了堵上边界突刺,地铁站的统计方式升级了:不再用“固定 5 分钟一块”的板子,而是把看板改成“永远统计过去 5 分钟内的总人数”,并且每秒滚动刷新一次。10:00:01 时看的是 9:55:01 到 10:00:01 的进站人数;到了 10:00:02,看板就自动平移为 9:55:02 到 10:00:02。窗口永远在滑动,任何一秒回头看,数据都是最近 5 分钟的真实流量,再也不存在“两个固定窗口交界处”的漏洞。
滑动窗口的常见实现有两种。日志法:每进来一个请求就记下当前时间戳,判断时把窗口外的时间戳删掉,剩下数量就是窗口内请求数;细分窗口法:把 5 分钟切成 60 个小格子,每格 5 秒,用一个环形数组滚动累加。两种方法各有取舍:日志法精度高但存储开销大,细分窗口法速度快但精度受格子数限制。
1. 小格子计数:每个时间小格内记录的请求数,用 cᵢ 表示。
2. 格子数量:窗口长度除以格子长度,决定了统计精度和内存开销。
3. 求和累加:把窗口内所有格子的计数相加。
4. 滚动窗口总数:得到当前时刻往前推一个窗口的总请求数。
滑动窗口用“滚动视角”解决了边界突刺,但并非没有代价:它需要更大的存储和更多的计算。地铁站要为每一秒的数据都保留一个统计格;系统里每多一个接口的限流,就要多一组时间格数组。这是典型的“用空间换精准”。
5. 漏桶限流:匀速旋转的刚性闸机
固定窗口和滑动窗口都在回答“这段时间内放多少人”,漏桶却换了一个思路:不管外面涌来多少人,我每分钟就匀速放那么多人出去,就像一只底部开了小口的桶——水以恒定的速度一滴一滴漏出去。桶里的水就是排队的请求,桶的容量就是最大等待数。请求来了先进桶排队,桶按固定速率放行;桶满了,再来的请求直接倒掉。
漏桶看起来和滑动窗口一样能限流,但本质截然不同:滑动窗口控制的是“总量”,漏桶控制的是“速率”。它最大的价值是削峰——把突发的流量脉冲变成平滑的匀速流。对下游的数据库和外部接口来说,匀速是最友好的输入:不会冷不丁被一波 1000 QPS 打挂。代价也很明显:它毫无弹性,就算系统当前很空闲,漏桶依然按老牛拉车的速度放行,突发流量一律排队,用户体验可能受损。
1. 放行间隔:每放出一个请求需要等待的固定时间间隔。
2. 恒定速率:每秒流出的请求数等于放行间隔的倒数。
3. 输入不影响输出:无论请求多猛烈,出口速率保持不变。
4. 桶满即丢:排队容量耗尽时,新请求被直接丢弃。
用一句话总结漏桶:它像一个固定口径的水龙头,无论上游水管里是涓涓细流还是滔天洪水,流进你杯子的水永远一样快。如果你需要绝对稳定的下游输出,漏桶是最可靠的选择。
6. 令牌桶限流:弹性放行的取票机
漏桶太“死板”了,即便系统很闲也不肯多放人。令牌桶算法则聪明地兼顾了稳定与弹性:地铁站闸机旁多了一台取票机,它每秒钟自动吐出一张“进站票”放进票盒,票盒最多放 10 张;乘客进站前必须先取一张票投进闸机。平时人流少,票盒里渐渐攒满 10 张;突然来一大波人,大家可以在几秒内把攒下的票全部用完,实现短时“放行加速”;票用完后,就只能按每秒一张的节奏进站。
这就是令牌桶的精髓:系统空闲时积攒令牌,突发时一次性消耗。判断是否放行时,先补充这段时间新长出来的令牌,再看桶里够不够用。令牌桶用两个参数分别控制两种性格:速率 r 决定长期平均吞吐,容量 C 决定短时突发上限。
1. 旧令牌数:上次补充后桶内剩余的令牌数量,用 T₀ 表示。
2. 新增令牌:按速率 r 在 Δt 秒内生成的新额度。
3. 合并累计:旧令牌加新令牌,得到理论上的令牌总量。
4. 容量封顶:超过桶容量 C 的部分被丢弃,令牌不会无限积攒。
秒杀场景偏爱令牌桶,因为它让系统在保护自己的同时,还能“配合演出”地扛住开抢瞬间的天量流量,而不是把所有人都拒之门外。r 大、C 小,系统平稳少波动;r 小、C 大,则能承受更强的瞬时冲击——调参,就是在稳定与弹性之间寻找平衡。
7. 分布式协同:多站点共写一本台账
当城市里多个地铁站共享同一本客流台账时,新问题出现了:A 站安检员和 B 站安检员同时看到“当前人数 500”并按下“+1“,看板真的会变成 502 吗?单块看板上的机械锁机制,在跨站点场景下还能生效吗?答案是:可以,但前提是把”读写同一块看板”变成唯一的权威路径。在分布式系统里,这个权威路径就是 Redis——所有实例都读写同一个 key,谁也别想在本地私自记账。
但这还不够。多实例同时执行“读计数、判断、写回”时,即使每个 INCR 都是原子的,组合操作依然可能被穿插。解决办法是用 Lua 脚本把整套逻辑打包,在 Redis 服务器端一次性执行,执行期间不会被其他命令打断——相当于把“看板操作规则”写成了不可撕毁的一页纸,谁来了都只能照章办事。另一方面,Redis 主从架构也带来一致性风险:主节点宕机时,未同步到从节点的计数会丢失;从节点在同步延迟期可能读到旧值。要求更高时,可以引入共识协议,保证“大多数节点都确认”才算成功。quorum(法定人数)机制就是如此。
1. 节点总数 n:参与共识决策的集群节点数量。
2. 半数向下取整:对 n/2 向下取整,得到不超过半数的最小整数。
3. 加一形成多数:在半数的基础上加 1,保证严格超过 50%。
4. 决策阈值:至少 Q 个节点确认,写入结果才能生效。
分布式协同的核心思想一句话:计数不是“A 站说了算”,而是“多个站点按同一套规则达成一致”。Redis 提供权威存储,Lua 保证操作原子,quorum 机制保证共识,三者叠加,分布式限流才有了真正可信的“总台账”。
8. 限流落地实践:从闸机到总控的完整防线
光在入口放一台令牌桶就万事大吉了吗?地铁站不会只依靠一道闸机:安检口外有波浪护栏做“粗限流”,站厅入口有电子看板做“分区统计”,站台门口还有工作人员手持喇叭动态调整放行速度。互联网系统同样需要多级防线——把限流部署在每一层,而不是只堵一个点。
典型的落地是分层的:最外层是 Nginx/API 网关,按来源 IP、用户 ID、接口路径做全局粗粒度限流,挡住洪水的大部分;中间业务服务层针对核心接口设置细粒度限流,比如下单接口 500 QPS、查询接口 2000 QPS;再配合监控系统实时观测计数器和系统负载,当发现某个实例异常,自动调低阈值或触发熔断降级。多层限流叠加后,最终生效的阈值取各层的最小值——就像地铁站里“安检只放 100 人”和“站台只收 80 人”同时作用,实际进站人数绝不会超过 80。
1. 网关阈值:入口网关层允许的最大请求速率,用 L_gw 表示。
2. 服务阈值:业务服务层为保护核心资源设置的限流值,用 L_svc 表示。
3. 取最小值:最终生效的限流阈值,是两层中较严的一档。
4. 互为兜底:即使某一层被绕过,另一层仍能保护系统不被冲垮。
从一块简单的电子看板,到固定窗口、滑动窗口、漏桶、令牌桶,再到 Redis + Lua + quorum 的分布式协同,最后到多级限流的完整防线——我们要做的,就是给系统装上一套聪明的地铁闸机系统。限流看似是“拒绝”,本质是“有管理地放行”。下一次流量洪峰来临时,你不再是站在站台中央慌张疏散人群的站长,而是坐在总控室里从容调度各入口闸机的指挥官。
AI 应用场景
场景一:电商秒杀系统的入口守护
阶段:系统架构设计 / 容量保障
解决的问题:秒杀开抢瞬间流量是平时的几百倍,若全部放行,订单服务、库存服务和数据库会被瞬间打满,导致下单超时、库存超卖甚至全站宕机。用令牌桶为秒杀接口设置弹性限额,允许开抢时消耗积攒的令牌,同时用漏桶保护写库链路,保证订单按恒定速率落地。
具体案例:某电商在大促时,网关层对秒杀接口配置 10 万 QPS 令牌桶,订单服务配置 5000 QPS 漏桶;Redis 计数与 Lua 脚本保证多机房实例共享同一份配额,页面通过排队进度条安抚用户,最终扛住了流量洪峰,交易成功率保持在 99.95%。
场景二:春晚红包与热搜的流量洪峰
阶段:线上稳定性治理 / 故障演练
解决的问题:热点事件瞬间制造巨大读流量,直接用数据库扛会引发缓存击穿和雪崩。用滑动窗口对读接口做精细限流,按窗口滚动的视角统计真实流量,在保证用户体验的同时保护下游资源。
具体案例:某内容平台在热搜爆发时,对 feed 接口采用滑动窗口限流(窗口 1 秒、格子 10 毫秒),并在 Redis 中共享窗口计数,保证多实例行为一致;同时埋点监控被限流比例,当限流比例超过 5% 时自动扩容或降级非核心功能。
场景三:开放平台 API 配额管理
阶段:多租户资源治理 / 流量运营
解决的问题:不同企业客户的 API 调用配额不同,需要为每个 client_id 维护独立的计数和限流阈值。用 Redis INCR 计数 + Lua 脚本判断配额,保证多个 API 网关实例共享同一份配额且不会被并发写乱。
具体案例:某开放平台为每个客户分配“每分钟 100 次”的配额,使用 Redis INCR + Lua 实现原子判断,超限返回 HTTP 429 并附带重试等待时间,既保证了租户间的公平性,又防止单个客户拖垮整体服务。
Python 演示
import time class TokenBucket: """令牌桶限流器:每秒补充 rate 个令牌,桶容量封顶""" def __init__(self, capacity, rate): self.capacity = capacity self.rate = rate self.tokens = capacity self.last_refill = time.time() def _refill(self): now = time.time() self.tokens = min(self.capacity, self.tokens + (now - self.last_refill) * self.rate) self.last_refill = now def allow(self, n=1): self._refill() if self.tokens >= n: self.tokens -= n return True return False class SlidingWindowLimiter: """滑动窗口限流器:基于请求时间戳日志""" def __init__(self, window_seconds, max_requests): self.window_seconds = window_seconds self.max_requests = max_requests self.requests = [] def allow(self): now = time.time() self.requests = [ts for ts in self.requests if now - ts < self.window_seconds] if len(self.requests) >= self.max_requests: return False self.requests.append(now) return True def demo(): bucket = TokenBucket(capacity=5, rate=2) print("=== 令牌桶:每 0.2s 发一个请求 ===") for i in range(10): time.sleep(0.2) print(f"请求{i + 1}: {'放行' if bucket.allow() else '拒绝'}") burst = TokenBucket(capacity=5, rate=1) print("\n=== 令牌桶:前两秒突发 8 个请求(攒了 5 个令牌)===") for i in range(8): print(f"请求{i + 1}: {'放行' if burst.allow() else '拒绝'}") window = SlidingWindowLimiter(window_seconds=5, max_requests=3) print("\n=== 滑动窗口:5 秒窗口限 3 个请求,每秒发 1 个 ===") for i in range(5): time.sleep(1) print(f"请求{i + 1}: {'放行' if window.allow() else '拒绝'}") if __name__ == "__main__": demo()
self.tokens = min(self.capacity, self.tokens + (now - self.last_refill) * self.rate)按流逝时间补充令牌,超出容量则封顶,这就是令牌桶的核心公式if self.tokens >= n:有足够令牌才放行,消耗后立即扣减self.requests = [ts for ts in self.requests if now - ts < self.window_seconds]只保留窗口内的时间戳,实现“窗口随时间滑动”的效果if len(self.requests) >= self.max_requests:窗口内请求已达上限则拒绝burst = TokenBucket(capacity=5, rate=1)初始令牌就是满的 5 个,所以突发前 5 个请求瞬间放行,第 6 个开始拒绝TokenBucket(capacity=5, rate=2)容量 5、每秒补充 2 个令牌,空闲时积攒能力决定了突发时的弹性
常见误区
误区限流会把用户请求直接丢掉,用户体验变差,所以限流阈值应该尽量调高,最好永远用不到。
正解限流像地铁站的波浪护栏,它不会让乘客的旅程消失,只是让他们晚一点到达。被限流的请求可以排队、重试或降级返回,换来的是绝大多数用户请求依然稳定可用。真正的问题不是“用了限流”,而是“该限流时没限流,结果全站宕机”。
为何常见:把“被限流”等同于“请求失败”,忽略了在系统过载时,拒绝少数请求恰恰是在保护更多请求的成功。
误区边界突刺是因为窗口太长,把窗口从 60 秒改成 10 秒,突发翻倍的时间窗就变小了,问题自然就弱化了。
正解边界突刺的本质是固定窗口的“重置效应”,无论窗口多短,只要窗口是固定的,相邻两个窗口交界处都可能涌入 2 倍流量。唯一彻底解决的办法是换用滑动窗口,让窗口边界永远在滚动。
为何常见:容易被“改小窗口 = 更精确”的直觉误导,没有理解突刺的根源在于窗口重置本身,而不是窗口大小。
误区两者都能把流量限制在某个速率内,效果差不多,选一个实现简单的就行。
正解漏桶强制匀速输出,绝对平滑,但系统空闲时也不能加速处理积压;令牌桶允许桶内积攒的令牌被一次性消耗,能短时突发,体验更好。保护数据库等脆弱下游选漏桶,兼顾用户体验和系统保护选令牌桶。
为何常见:只看到“限流”二字,忽略了它们对突发流量的态度截然不同——一个削峰,一个蓄峰。
误区INCR 命令本身是原子操作,所以只要用 INCR 计数,主从切换、超时重试都不会出问题。
正解INCR 的原子性只保证单条命令内部不出错。主从复制延迟时,从节点可能是旧值;主节点宕机切换后,未同步的计数会丢失;客户端超时重试还可能导致同一请求被计两次。需要结合业务对一致性的要求,配合 Lua 脚本、幂等键或共识协议来解决。
为何常见:把“原子性”理解成了“完整的一致性保证”,混淆了单机原子性与分布式一致性这两个层次的问题。
面试题
Q1. 令牌桶限流最核心的特点是?
- A.请求以恒定速率被处理
- B.允许一定程度的突发流量← 正确答案
- C.令牌生成后永不消失
- D.拒绝请求立即恢复计数
答案:B。令牌桶在系统空闲时积攒令牌,突发时可以一次性消耗,所以允许短时突发。A 是漏桶的特点;C 错误,令牌受容量上限约束;D 是对行为的误解,被拒绝不会立即恢复计数。
Q2. Redis 的 INCR 命令用于分布式计数的最大优势是?
- A.执行速度快于普通命令
- B.原子性保证并发不错乱← 正确答案
- C.支持事务回滚的语义
- D.自动生成全局唯一的ID
答案:B。INCR 把“读-加-写”捆绑成一个原子操作,并发环境下不会出现覆盖写,这才是它适合做分布式计数的根本原因。A 不是决定性优势;C 错误,Redis 事务不保证回滚;D 只是 INCR 的衍生用途。
Q3. 固定窗口限流的“边界突刺”是指?
- A.窗口计数超过阈值
- B.边界前后涌入两倍流量← 正确答案
- C.窗口内请求被丢弃
- D.计数器溢出导致崩溃
答案:B。两个固定窗口交接的瞬间,前一个窗口的剩余配额和新窗口的满额配额可以接连使用,短时间内最多涌入接近 2 倍阈值。A 是限流正常生效;C 描述不清;D 是另一个问题。
Q4. 下列属于滑动窗口限流实现思路的是?
- A.记录请求的时间戳日志← 正确答案
- B.用令牌填充满窗口消耗速度
- C.将窗口拆分为多个小窗口← 正确答案
- D.每请求重置整个窗口计数
答案:AC。日志法记录每个请求的时间戳并滚动清理,是滑动窗口的经典实现;细分窗口法把大窗口拆成小格子滚动求和,也是常见实现。B 是令牌桶的思路;D 会破坏窗口的连续性,属于错误实现。
Q5. 漏桶限流最典型的特征是?
- A.允许瞬时突发流量
- B.输出速率恒定不变← 正确答案
- C.输入越快输出越快
- D.桶空时请求全部拒绝
答案:B。漏桶的出口速率是固定的,无论输入多么猛烈,输出永远匀速。A 是令牌桶的特征;C 恰恰相反;D 错误,桶空只是没有积压,新请求仍可正常流出。
Q6. 分布式限流与单机限流的本质区别是?
- A.分布式限流吞吐量更高
- B.计数状态需要跨节点共享← 正确答案
- C.分布式限流没有时间窗口
- D.分布式限流不产生延迟
答案:B。分布式限流的核心难点在于多个实例必须共享同一份计数状态,否则各实例各自计数会导致总流量失控。A 不是必然的;C、D 与分布式限流实现无关。
Q7. 关于 Redis 主从架构计数一致性问题,下列说法正确的有?
- A.主节点切换可能丢失未同步计数← 正确答案
- B.从节点可能读到过期的计数值← 正确答案
- C.Lua 可彻底解决主从延迟问题
- D.合理配置可降低不一致风险← 正确答案
答案:ABD。主从复制是异步的,主节点宕机时未同步的计数会丢失,切换期间从节点也可能读到旧值,所以 A、B 正确。Lua 只能保证单条脚本的原子性,无法消除主从复制延迟,C 错误。通过合理配置主从策略、配合降级兜底,可以降低不一致带来的影响,D 正确。
Q8. 秒杀场景选令牌桶而非漏桶,主要原因是令牌桶?
- A.代码实现相对更简单
- B.能响应短时突发流量← 正确答案
- C.不需要中心化存储
- D.可以杜绝商品超卖
答案:B。秒杀开抢的瞬间流量最猛,令牌桶可以把空闲时积攒的令牌一次性消耗,让系统“扛住”这波突发。A 不成立,两者实现难度相当;C 错误,分布式限流都要中心化存储;D 与限流无关,超卖要靠库存一致性解决。
Q9. 多实例限流时,为了保障计数正确,以下做法合理的有?
- A.读写都走 Redis 主节点← 正确答案
- B.使用 Lua 脚本保证原子性← 正确答案
- C.每次请求都查从节点提升性能
- D.配合幂等设计降低重试误差← 正确答案
答案:ABD。强一致场景下读写主节点能拿到最新计数,A 正确;Lua 脚本把“读判写”打包成原子操作,B 正确;从节点有同步延迟,读到的可能不是最新计数,会影响限流精度,C 错误;超时重试可能造成重复计数,用幂等键可以规避,D 正确。
Q10. 用滑动窗口限流一个读多写少的接口,最合理的存储选择是?
- A.时间戳入消息队列异步处理
- B.计数存 Redis 并定期清理过期← 正确答案
- C.仅存服务本地内存数组
- D.写数据库再全量统计
答案:B。多实例共享计数必须用 Redis,滑动窗口数据存 Redis 并定期清理过期时间戳是标准做法。A 异步处理会让限流判断滞后,失去意义;C 只在单机生效;D 全量写库统计太重,无法支撑高并发。