用Redis做请求限流到底靠谱吗,性能和实现细节聊聊
- 问答
- 2026-01-01 21:43:24
- 3
用Redis做请求限流是靠谱的,而且在业界是一种非常常见和成熟的方案,很多大公司都在用,比如阿里云、腾讯云他们自己提供的限流服务,底层思想也常常能看到Redis的影子,但“靠谱”不代表可以无脑用,它的性能表现和最终是否靠谱,完全取决于你的实现细节和Redis本身的使用方式,下面我们就详细聊聊这件事。
为什么Redis适合做限流?
限流的核心是“计数”,而且要保证这个计数在分布式环境下是准确无误的,你想限制某个API每分钟只能被一个用户访问1000次,如果只有一台服务器,你用内存里的一个变量就能计数,但现在你的应用部署在十台机器上,用户的请求可能被分发到任何一台,每台机器自己数自己的,根本没法知道总数,这时候就需要一个“中央计数器”,所有服务器都向它汇报和查询,Redis作为一个内存数据库,读写速度极快(能达到每秒十万次甚至更高级别的操作),而且天生支持分布式访问,完美地扮演了这个“中央计数器”的角色。
常见的限流算法用Redis怎么实现?
-
固定窗口计数器法 这是最简单的一种,比如限制每分钟100次请求,我们可以在Redis里设置一个键,
rate_limit:user123:minute,并给它设置一分钟的过期时间,每次请求来了,就执行一个INCR命令(让值加1),如果返回的值大于100,就拒绝请求;INCR返回1,说明是这一分钟的第一个请求,同时用EXPIRE命令设置过期时间为60秒。- 优点:实现简单,Redis操作少,性能损耗小。
- 缺点:有临界问题,假设在59秒和61秒这两个时间点,各自来了100个请求,因为在59秒时属于第一个窗口,计数到100;61秒时窗口重置,又从1开始计数,这样在59秒到61秒这两秒内,实际通过了200个请求,超过了我们一分钟100的限制,不够平滑。
-
滑动窗口法 这个方法更精确,它把时间窗口划分成更细的格子,比如一分钟的窗口,我们分成60个格子,每个格子代表1秒,用一个Redis的列表(List)或有序集合(ZSet)来记录每次请求的时间戳,每次新请求到来时:
- 移除所有超过一分钟前的时间戳(比如用
ZREMRANGEBYSCORE命令)。 - 计算当前集合中剩余的时间戳数量(比如用
ZCARD命令)。 - 如果数量小于限制数(100),就把当前时间戳加入集合,允许请求;否则就拒绝。
- 优点:解决了固定窗口的临界问题,限流更精准。
- 缺点:比固定窗口复杂,需要存储所有在时间窗口内的请求时间戳,如果并发量极大,这个集合会比较大,消耗更多内存,而且每次请求需要执行两个Redis操作(移除旧记录和添加新记录),对Redis的压力更大。
- 移除所有超过一分钟前的时间戳(比如用
-
令牌桶算法 这是更高级、更平滑的方法,想象有一个桶,以固定速率(比如每秒2个)放入令牌,桶最多能装100个令牌,请求来了就从桶里拿走一个令牌,有令牌就能通过,没令牌就拒绝,用Redis实现可以用一个键存当前令牌数,另一个键存最后补充令牌的时间戳,每次请求来时:
- 计算自上次更新到现在,应该补充多少令牌(当前时间 - 最后更新时间 * 放入速率)。
- 将当前令牌数更新为(补充后的令牌数)和(桶容量)中的较小值。
- 如果令牌数大于等于1,就减1,允许请求。
- 优点:既能限制平均速率,又能允许一定程度的突发流量(因为桶里有积攒的令牌),非常实用。
- 缺点:实现最复杂,需要保证“计算并补充令牌”和“消费令牌”这两个步骤是原子性的,否则在并发下会出错,通常需要用到Redis的Lua脚本(后面会讲)来保证原子性。
性能和实现上的关键细节
这才是决定Redis限流是否真正“靠谱”的重中之重。
-
原子性是命根子 在高并发下,多个请求可能同时去检查计数和增加计数,如果不是原子操作,就会导致超限,当前计数是99,两个请求同时来,都检查到99<100,然后都执行
INCR,结果变成了101,两个请求都通过了,这就超限了。- 解决方案:必须使用Redis的原子操作,对于简单的
INCR,它本身是原子的,但对于像令牌桶那种需要“先计算再判断再设置”的多步操作,就必须使用 Lua脚本,Redis会单线程执行整个Lua脚本,中间不会被其他命令打断,从而完美解决并发问题,这是实现生产级别限流必须要用的技术。
- 解决方案:必须使用Redis的原子操作,对于简单的
-
网络开销是瓶颈 每一次限流检查,都意味着一次甚至多次Redis网络请求,如果你的业务本身逻辑很简单,但每次处理都要去远程Redis查一下,这个网络延迟(通常零点几到几毫秒)可能会成为主要的性能瓶颈。
- 解决方案:
- 使用管道(Pipeline):如果在一个业务流程中需要做多次Redis操作,可以把它们打包成一个管道一次发送,减少网络往返次数。
- 让Redis离应用服务器更近:部署在同一机房或同一个可用区内,降低网络延迟。
- 考虑使用本地缓存+Redis的双层方案:在每台应用服务器本地,先做一个更宽松的限流(比如每秒500次),超过这个数再去查询Redis做全局严格的限流(比如每秒1000次),这样大部分请求在本地就被处理了,极大减轻Redis的压力和网络开销,但这会增加系统的复杂性。
- 解决方案:
-
Redis本身的高可用 如果你的限流完全依赖一个Redis单点实例,那么这个实例一旦挂掉,所有服务可能都会因为无法限流而直接放行所有请求,导致后端服务被压垮,或者因为限流逻辑失败而拒绝所有请求,导致服务完全不可用。
- 解决方案:必须使用Redis集群或哨兵(Sentinel)模式,保证高可用,在代码里要做好降级策略,当检测到Redis连接超时或失败时,可以 fallback 到本地的限流方案,或者直接记录日志并放行,根据业务场景选择“宁可错放”还是“宁可错杀”。
总结一下
回到最初的问题:用Redis做请求限流靠谱吗? 答案是:方案本身非常靠谱,是经过大规模实践验证的,但你的实现是否靠谱,取决于你是否处理好了以下问题:
- 选对算法:根据你对精准度的要求,选择固定窗口、滑动窗口还是令牌桶。
- 保证原子性:对于复杂逻辑,坚决使用Lua脚本。
- 优化性能:意识到网络开销,并通过管道、就近部署等方式缓解。
- 保证可靠性:使用Redis高可用集群,并设计好降级方案。
只要你把这些细节都考虑周全并实现好,Redis就是一个非常强大且可靠的分布式限流工具。

本文由雪和泽于2026-01-01发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/72691.html
