用Redis怎么搞计数限流,感觉挺实用也不难理解
- 问答
- 2026-01-15 23:01:26
- 2
你说得对,用Redis做计数限流确实非常实用,而且核心思想不难理解,咱们就把它想象成一个简单的“令牌桶”或者“访问次数记录本”,只不过这个本子放在了速度极快的Redis里。
核心思想:简单粗暴的计数
想象一下,你要给某个API接口限流,比如一分钟内最多允许访问100次,没有Redis的时候,你可能会把计数存在程序的内存里,但如果你有多台服务器,这个计数就不准了,因为每台服务器都有自己的小本本,没法统一。
这时候Redis就派上用场了,我们可以把Redis当成一个唯一的、速度超快的中央计数器,所有的服务器在接到API请求时,都去问这个中央计数器:“喂,现在还能不能访问?” Redis负责统一记录和判断。
最基本的实现方式:固定窗口计数法
这是最直观的方法,就像把时间切成一个个固定的窗口(比如1分钟一个窗口)。
- 设计Key:我们需要一个唯一的键(Key)来标识这个限流规则,我们可以把接口名和当前的时间窗口绑定在一起,对于接口
/api/v1/user,它的Key可以是rate_limit:/api/v1/user:202310261355(2023年10月26日13点55分这一分钟),这样,每一分钟都会有一个全新的Key。 - 操作过程:
- 第一步:计数加一,每当一个请求过来,我们就对这个Key执行一个
INCR命令。INCR是Redis里一个非常简单的命令,就是把某个Key的值增加1,如果这个Key不存在,Redis会先把它创建出来,值设为0,然后再加1,所以返回值是1。 - 第二步:设置过期时间,当我们第一次创建这个Key(也就是
INCR返回1)的时候,我们需要给这个Key设置一个过期时间(TTL),比如60秒,这样,一分钟过后,这个Key就会自动被Redis删除,下一分钟的计数又从零开始,设置过期时间用的是EXPIRE命令。 - 第三步:判断是否超限。
INCR命令会返回增加后的值,我们拿到这个返回值,比如是current_count,然后我们和设定的阈值(比如100)比较。current_count > 100,就说明这一分钟内的访问量已经超过100次了,这时候就应该拒绝这个请求(比如返回HTTP 429 Too Many Requests),如果小于等于100,就正常处理。
- 第一步:计数加一,每当一个请求过来,我们就对这个Key执行一个
这个方法的好处是超级简单,只用到了Redis两个基本命令INCR和EXPIRE,性能非常高。
但有个明显的缺点:窗口临界问题
固定窗口法有个硬伤,想象一下,限流是每分钟100次。
- 在13:59:30这一秒开始,突然来了50个请求。
- 紧接着,在14:00:01这一秒(也就是下一分钟的开始),又瞬间来了50个请求。
虽然这两个时间点都属于不同的“一分钟窗口”,各自窗口内的请求数(50)都没有超限,但从用户的角度看,在短短两秒内(13:59:30到14:00:01),系统实际接收了100个请求,这就在时间窗口的边界处形成了一个流量尖峰,可能还是会压垮系统。
更平滑的解决方案:滑动窗口计数法
为了解决边界问题,人们想到了滑动窗口,它不像固定窗口那样“一刀切”地切换时间,而是像一个窗口一样在时间轴上滑动,看的是一段连续的时间。
实现滑动窗口有多种方式,一种比较常见且仍然简单的是使用Redis的有序集合(Sorted Set)。
- 设计Key:这次我们不需要把时间写在Key里了,一个接口就用一个Key,
rate_limit_sliding:/api/v1/user。 - 操作过程:
- 第一步:记录时间戳,当一个请求到来时,我们获取当前的时间戳(精确到毫秒最好),比如是
1698301234500,这个时间戳将作为有序集合里的“分数”(Score)。 - 第二步:清理过期请求,我们定义时间窗口的大小,比如一分钟(60000毫秒),当前时间戳减去60000毫秒之前的所有请求记录都是过期的,我们用
ZREMRANGEBYSCORE命令,删除有序集合中分数小于(当前时间戳 - 60000毫秒)的所有成员,这一步保证了我们集合里只保留最近一分钟的请求记录。 - 第三步:判断当前请求数,使用
ZCARD命令,获取当前有序集合的成员数量,这个数量就是最近一分钟内的请求总数。 - 第四步:判断是否超限,如果请求总数已经大于等于阈值(100),就拒绝请求。
- 第五步:添加当前请求,如果还没超限,就用
ZADD命令,将当前请求的时间戳(作为分数)和一个唯一值(比如可以用UUID,或者直接用时间戳也行)作为成员,添加到有序集合中。 - 第六步:设置整体过期时间:为了不让这个有序集合无限变大,我们还可以给它设置一个过期时间,比如比窗口时间长一点(70秒),让它能自动清理。
- 第一步:记录时间戳,当一个请求到来时,我们获取当前的时间戳(精确到毫秒最好),比如是
滑动窗口这种方式解决了临界突变的问题,因为它统计的是任何一个时间点开始往前推一分钟内的请求总量,流量控制更加精确和平滑,代价是比固定窗口法稍微复杂一点,用了更多的Redis命令。
实际应用中的技巧和选择
在实际项目中,你可能会直接使用一些现成的库,比如Redis官方的RedisCell模块(它实现了更高级的漏桶算法),或者在Spring Cloud Gateway、Nginx等网关层面直接配置限流规则。
但理解这两种基本的Redis实现方式非常有价值,因为它揭示了限流的核心,选择哪种方式取决于你的业务场景:
- 如果对限流的精确性要求不是极高,可以接受短暂的流量毛刺,那么固定窗口的简单性和高性能是很有吸引力的。
- 如果需要对流量进行更平滑、更精确的控制,避免临界问题,那么滑动窗口是更好的选择。
希望这个直接的解释能让你对“用Redis搞计数限流”有一个清晰实用的认识。

本文由颜泰平于2026-01-15发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/81436.html
