当前位置:首页 > 技术学院 > 技术前线
[导读]在分布式系统的高并发场景下,限流是守护服务稳定性的最后一道防线。当突发流量、恶意爬虫或者接口刷量请求涌入时,没有限流保护的后端服务很容易在短时间内被打垮,出现数据库连接池耗尽、CPU占用率飙升、核心业务不可用等严重故障。而Redis凭借其毫秒级的读写性能、丰富的数据结构和天然的分布式特性,成为了业界实现限流方案的首选载体。不同的限流方案适配不同的业务场景,从最简单的固定窗口计数器,到兼顾精度与性能的滑动窗口,再到平滑流量的令牌桶,三种主流方案各有优劣,吃透它们的实现原理、适用边界和优化技巧,才能在实际项目中选出最适配业务需求的限流策略。

在分布式系统的高并发场景下,限流是守护服务稳定性的最后一道防线。当突发流量、恶意爬虫或者接口刷量请求涌入时,没有限流保护的后端服务很容易在短时间内被打垮,出现数据库连接池耗尽、CPU占用率飙升、核心业务不可用等严重故障。而Redis凭借其毫秒级的读写性能、丰富的数据结构和天然的分布式特性,成为了业界实现限流方案的首选载体。不同的限流方案适配不同的业务场景,从最简单的固定窗口计数器,到兼顾精度与性能的滑动窗口,再到平滑流量的令牌桶,三种主流方案各有优劣,吃透它们的实现原理、适用边界和优化技巧,才能在实际项目中选出最适配业务需求的限流策略。

固定窗口计数器限流:最简单高效的入门级方案

固定窗口限流的核心逻辑非常直观:把时间轴按照固定的粒度切分成一个个独立的时间窗口,比如1秒、1分钟或者1小时,在每个独立的窗口内统计用户或接口的请求次数,一旦请求数超过预先设定的阈值,就直接拒绝后续请求,直到下一个新的窗口重新开始计数。

它的实现逻辑几乎没有任何复杂度:我们用Redis的String类型做计数器,每次收到请求时调用INCR命令对指定的限流键做自增操作。如果自增后的结果刚好等于1,说明这是当前窗口的第一个请求,我们就给这个键设置一个和窗口时长相等的过期时间。最后只需要判断自增后的数值是否小于等于预设的阈值,就能决定是否放行当前请求。整个实现只需要几行核心代码,没有任何复杂的逻辑,性能极高,哪怕是每秒上万次的请求,Redis也能轻松承载。

这种方案的优势在于资源消耗极低,不需要维护额外的复杂数据结构,部署和调试成本几乎为零,非常适合业务流量分布均匀、对限流精度要求不高的场景。比如小型网站的普通接口限流、后台管理系统的登录请求频率控制,这类场景下固定窗口限流完全可以满足需求,不会出现明显的问题。

但它的致命缺陷是存在经典的“临界突增问题”:假设我们设置1分钟内最多允许100次请求,在第一个窗口的最后1秒,用户发起了100次请求,紧接着下一个窗口的第1秒又发起了100次请求,相当于在短短2秒的时间内系统收到了200次请求,远超每分钟100次的限流阈值。这种边界处的流量突增,很容易把没有做好冗余保护的服务直接打垮,在高并发的核心业务场景下,这个缺陷是完全不可接受的。

滑动窗口限流:解决边界突增的高精度方案

为了弥补固定窗口的临界突增缺陷,滑动窗口限流应运而生。它不再把时间切割成一个个互不重叠的固定窗口,而是维护一个持续向前滑动的动态时间窗口,比如“最近60秒”这个不断移动的时间区间,只要落在这个区间内的请求都会被统计,从根源上避免了窗口切换时的流量漏洞。

滑动窗口的标准实现依赖Redis的有序集合ZSet数据结构:我们把每个请求的唯一标识(可以直接用当前请求的时间戳作为唯一值)作为ZSet的元素,同时把当前请求的时间戳作为元素的score值。每次收到新请求时,首先执行ZREMRANGEBYSCORE命令,把ZSet里所有score值小于“当前时间戳减去窗口时长”的过期元素全部删掉,清理掉窗口之外的历史请求记录。之后统计当前ZSet里剩余的元素总数量,如果数量小于限流阈值,就把当前请求的时间戳作为新元素插入ZSet,直接放行请求;如果数量已经超过阈值,就直接拒绝当前请求。

这种方案的限流精度极高,完全不存在固定窗口的边界突增问题,能精准控制任意连续时间区间内的请求总数,非常适合对流量稳定性要求高的核心业务场景。比如电商的商品下单接口、支付系统的回调接口,这类场景下绝对不允许短时间内出现流量突增,滑动窗口限流可以完美满足需求。

当然它也有自己的短板:如果限流窗口很大、阈值很高,ZSet里需要存储大量的请求记录,会占用较多的Redis内存资源。不过在实际业务中,绝大多数场景的限流窗口都不会超过几分钟,配合定期清理过期元素的操作,内存消耗完全在可控范围内。现在很多网关级别的限流组件,比如OpenResty的限流模块,底层都是基于滑动窗口的逻辑实现的,在互联网大厂的高并发系统里已经得到了非常广泛的验证。

令牌桶限流:实现流量平滑的工业级方案

前面两种限流方案都是“控制请求总数”,但在很多真实场景下,业务不仅要求限制总请求数,还希望流量的到来是平滑均匀的,避免出现大量请求在同一时刻集中涌入的“流量毛刺”,这时候令牌桶限流就是最优选择。

令牌桶的核心逻辑非常巧妙:我们以一个固定的速率往桶里放入令牌,比如每秒生成10个令牌,桶本身有一个最大容量。每个请求想要被放行,必须先从桶里拿到一个可用的令牌,如果桶里已经没有令牌了,就直接拒绝请求。这种机制天然就实现了两个核心特性:一方面长期来看,请求的平均速率不会超过令牌生成的速率,满足限流的要求;另一方面桶可以积累一定数量的令牌,允许短时间的突发流量通过,兼顾了系统的弹性和稳定性。

基于Redis的令牌桶实现,我们通常用Hash结构来存储桶的两个核心状态:桶里当前剩余的令牌数量,以及上一次补充令牌的时间戳。每次收到请求时,首先根据当前时间戳和上一次补充令牌的时间差,计算出这段时间里应该新生成的令牌数量,把令牌数补充到桶里,注意不能超过桶的最大容量。之后判断剩余令牌数是否大于等于1,如果是就消耗一个令牌,放行当前请求,更新剩余令牌数和时间戳;如果剩余令牌数为0,就拒绝请求。

令牌桶的优势在于它能把不规则的突发流量,整形为相对平滑的流量输出,非常适合网关层的全局限流场景,比如API网关的出口流量控制、微服务之间的调用频率管控。它既可以限制长期的平均调用速率,又不会完全拒绝合理的突发请求,是现在分布式系统网关限流的主流工业级方案。

三种基于Redis的限流方案,没有绝对的优劣之分,只有适配场景的区别。固定窗口适合简单轻量的低并发场景,滑动窗口适合高精度的核心业务,令牌桶适合需要流量整形的网关层场景。在实际生产环境中,我们还可以结合Lua脚本把整个限流逻辑封装成原子操作,避免并发场景下的竞态问题,再配合Redis集群实现水平扩展,完全可以支撑起十万甚至百万级QPS的高并发限流需求,为整个分布式系统构建起一道坚固的流量防护墙。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

Redis作为高性能的内存数据库,在缓存、会话存储、消息队列等场景中广泛应用。然而,随着业务规模扩大,Redis的配置、使用和维护面临诸多挑战。

关键字: Redis 数据库

在分布式系统成为主流的今天,传统的会话管理机制已难以满足跨域、跨服务的身份验证需求。JWT(JSON Web Token)作为一种轻量级身份验证方案,正以"自包含令牌"的特性重塑网络认证体系。

关键字: 分布式系统 JWT

嵌入式中间件与软总线作为现代分布式系统的核心基础设施,对于降低系统开发复杂度、实现异构环境互操作至关重要。文章系统梳理了应用服务器、远程过程调用(RPC)、消息中间件、容器编排平台等主流中间件以及新兴软总线技术的发展脉络...

关键字: 嵌入式软件 软件中间件 软总线 分布式系统

Redis 作为高性能键值存储系统,其设计细节中蕴含着对效率与灵活性的深刻考量。默认配置的 16 个数据库(编号 0-15)便是这一理念的典型体现。

关键字: Redis 数据库

在分布式系统与微服务架构成为主流的今天,RESTful API 作为前后端分离的核心通信方式,其设计质量直接关系到系统的稳定性和用户体验。幂等性(Idempotence)作为 RESTful API 设计的核心原则之一,...

关键字: 幂等性 分布式系统

在分布式系统中,数据一致性是核心挑战之一。由于节点故障、网络延迟或分区等异常情况,确保多个节点间数据同步成为关键问题。一致性协议算法通过协调节点行为,在保证系统可用性的同时,维护数据的一致性。本文将深入解析六种经典的一致...

关键字: 分布式系统 2PC

在物联网和分布式系统快速发展的今天,跨平台代码的可移植性已成为软件开发的核心挑战之一。不同硬件架构(x86、ARM、RISC-V)和操作系统(Linux、Windows、RTOS)在数据表示方式上存在显著差异,其中字节序...

关键字: 跨平台编程 物联网 分布式系统

在Redis中,有三种不同的部署模式:主从复制(Master-Slave Replication)、哨兵(Sentinel)模式和集群(Cluster)模式。每种模式都有其特定的用途和优势,适用于不同的场景。

关键字: Redis 集群

编者按:亚马逊云科技是唯一一家如此大规模使用自动推理的云提供商。随着越来越多的人使用自动推理工具,这让我们在提升自动推理工具的可用性和可扩展性上更容易进行大量的投入。我们发现自动推理工具越易于使用,它们的功能就会变得越强...

关键字: 分布式系统 自动推理工具

Redis是一款高性能、开源的内存数据库,同时也支持将数据保存在磁盘上。其主要用途是通过缓存及存储常用数据来提高应用的性能。相较传统的关系型数据库,Redis在读写大量数据的场景中更具优势,它可以提供更高的性能及更低的延...

关键字: Redis MySQL
关闭