限流算法
- 滑动窗口算法(Sliding Window)
- 工作机制
- 核心特性
- 令牌桶算法(Token Bucket)
- 工作机制
- 简单方式理解机制
- 具体实例
- 核心特性
- 漏桶算法(Leaky Bucket)
- 工作机制
- 核心特性
- 面试中实际问题
- 1 令牌桶允许突发流量,但漏桶不行
- 2 在工程实现中,滑动窗口限流通常会结合 Redis 的 ZSet 记录请求时间戳,从而统计最近时间窗口内的请求数量
- Redis 是什么?
- 示例说明
- Redis为什么适合做限流?
- ZSet 是什么?
- 滑动窗口限流
- 3 在分布式场景下,还需要通过 Redis 实现全局一致的限流策略,避免多实例情况下整体流量失控。
滑动窗口算法(Sliding Window)
定义:滑动窗口算法通过在连续时间区间内统计请求数量,并与预设阈值进行比较,以判断是否允许请求通过的一种限流算法。
工作机制
将时间划分为连续滑动的时间窗口,统计窗口内的请求数
若请求数 ≤ 阈值 → 放行
若请求数 > 阈值 → 拒绝 / 限流
📌 常见实现方式:
滑动时间窗口计数
滑动窗口日志(request log)
核心特性
能平滑限制突发流量
精度高于固定窗口
实现复杂度相对较高
令牌桶算法(Token Bucket)
定义:令牌桶算法通过以固定速率向桶中生成令牌,请求必须获取令牌才能被处理,从而控制系统的平均请求速率并允许一定程度的突发流量。
工作机制
系统以固定速率向桶中添加令牌,桶有最大容量,每个请求消耗一个令牌
无令牌 → 请求被拒绝或阻塞
简单方式理解机制
令牌桶 = 用“令牌”控制速度,有令牌就放行,没令牌就拒绝
把令牌桶“实体化”来看
现在脑子里就想 三个东西:
🪣 一个桶
🪙 桶里放的是令牌
⏱ 系统按固定速度往桶里放令牌
完整工作流程
✅ 第 1 步:系统不断“发令牌”
系统 每秒生成固定数量的令牌,比如:每秒 5 个令牌
桶有上限,比如:最多 10 个令牌
📌 如果桶满了:新生成的令牌 直接丢弃,桶里最多只能有 10 个令牌
✅ 第 2 步:请求来了,先“拿令牌”
每来 1 个请求,必须先从桶里拿 1 个令牌
情况分两种:
情况 A:桶里有令牌 ✅→ 拿走 1 个,请求放行
情况 B:桶里没令牌 ❌→ 拿不到, 请求被拒绝 / 限流
✅ 第 3 步:关键点
❗ 请求快不快,取决于桶里有没有令牌,而不是“现在这一秒”
具体实例
假设规则是:令牌生成速率:2 个 / 秒,桶容量:6 个
🕐 场景 1:系统空闲了一会儿,桶里攒满了:6 个令牌。 这时突然来了 6 个请求,一瞬间!每个请求拿 1 个令牌,6 个全放行 ✅
📌 这就是:允许突发流量
🕐 场景 2:又突然来了第 7 个请求。桶已经空了,没有令牌 ,第 7 个请求被拒绝。
🕐 场景 3:过了 1 秒,系统又生成 2 个令牌。现在 最多只能再放行 2 个请求
核心特性
限制平均速率
允许短时间突发流量
广泛用于 API 网关、微服务限流
漏桶算法(Leaky Bucket)
定义:漏桶算法将请求视为水流注入桶中,并以固定速率从桶中漏出请求,从而实现对输出流量的强制平滑控制。
工作机制
请求先进入“桶”(队列),桶以恒定速率处理请求
桶满 → 新请求被丢弃
核心特性
输出速率恒定
不允许突发流量
强调系统稳定性而非吞吐峰值
面试中实际问题
1 令牌桶允许突发流量,但漏桶不行
令牌桶通过令牌累积机制,允许在短时间内消耗已积累的令牌,从而支持突发流量;而漏桶以固定速率处理请求,输出速率恒定,因此无法处理突发流量。
2 在工程实现中,滑动窗口限流通常会结合 Redis 的 ZSet 记录请求时间戳,从而统计最近时间窗口内的请求数量
Redis 是什么?
Redis 本质上就是一个运行在某台服务器上的数据库软件,其他服务器通过网络访问它。
Redis 是一种独立部署的内存型 NoSQL 数据库,通常运行在专用服务器或集群节点上,为多个应用实例提供共享数据访问能力。
Redis 在做一件事:帮我们记住:每一次请求是什么时候来的
因为请求很多,机器很多,需要一个 大家共用的、很快的记事本
👉 Redis 就是这个「高速共享记事本」
示例说明
假设现在有3 台「业务服务器」,服务器 A,服务器 B,服务器 C,它们都在对外提供服务(接用户请求)。👉 但它们彼此之间是不共享内存的,A 不知道 B 在干嘛, B 也不知道 C 的状态。那问题来了:「大家怎么用同一本账?」
比如限流时要知道:“现在一共来了多少请求?”
如果每台服务器自己记:A:我这里 30,B:我这里 30,C:我这里 30。👉 实际已经 90 了,但没人知道 ❌
可以这样理解 Redis:Redis = 放在“中间”的一个公共记事本
系统结构是这样的:
服务器 A ─┐ 服务器 B ─┼──→ Redis(公共账本) 服务器 C ─┘Redis 自己跑在一台服务器上。所有业务服务器通过网络去读 / 写 Redis👉 所以它们看到的是同一份数据
Redis为什么适合做限流?
1️⃣ 第一原因:快
Redis 在内存里👉 查一次几乎不花时间
专业说法:Redis 是内存数据库,单次读写延迟在微秒级,非常适合高频访问场景。
2️⃣ 第二原因:大家都能用同一个 Redis
多台服务器都能看同一本“账本”
否则会发生:
A 服务器觉得还能放
B 服务器也觉得还能放
合起来就超了 ❌
专业说法:Redis 作为集中式存储,可用于实现分布式系统中的全局限流。
3️⃣ 第三原因:操作是“原子的”
什么叫原子操作?一件事要么完整成功,要么完全不发生,中间不会被别人插队。比如:“先加 1,再判断”这两步在 Redis 里可以当成 一步
📌 这对限流非常重要。否则会出现:两个请求同时通过 ❌
专业说法:Redis 提供原子操作和 Lua 脚本机制,可保证限流逻辑在高并发下的正确性。
ZSet 是什么?
ZSet(有序集合)= 自动按时间排序的列表
| 请求 | 时间 |
|---|---|
| 请求 A | 10:00:01 |
| 请求 B | 10:00:05 |
| 请求 C | 10:00:30 |
Redis 会自动将请求按时间戳Score排好
滑动窗口限流
每来一个请求,记下来: “这个请求是几点来的”
删掉旧的:
👉 “60 秒以前的都不算了”
数一数现在还有多少条
超过上限就拒绝
3 在分布式场景下,还需要通过 Redis 实现全局一致的限流策略,避免多实例情况下整体流量失控。
在分布式系统中,单机限流只能控制局部流量,容易导致整体流量失控,因此需要通过 Redis 等共享存储实现全局限流,并保证限流逻辑的原子性和一致性。