当前位置: 首页 > news >正文

Redis 实现限流功能的几种方法

一、问题背景

在实际业务场景中,限流是保护系统的重要手段:在一段时间(period)内,限定某个行为(action)的最大次数(max_count)。本文介绍如何基于 Redis 实现多种限流方案。


二、限流类型总览

限流类型核心思想优点缺点
固定窗口限流时间窗口固定,到期自动清零实现简单存在窗口边界突击流量问题
滑动窗口限流窗口随时间滑动,统计窗口内请求数精确解决边界问题实现稍复杂
漏斗限流容量固定,速率固定精确控制容量和速率需要 Redis 模块支持
令牌桶限流令牌以固定速率放入桶中支持突发流量实现复杂

三、固定窗口限流

3.1 什么是固定窗口限流

将时间划分为固定的窗口,例如每 5 分钟为一个窗口:

|---5min---|---5min---|---5min---|---5min---| 20:00 20:05 20:10 20:15

在每个窗口内独立计数,窗口到期后计数清零。

3.2 Redis 实现
-- 固定窗口限流实现localkey="***"..user_id..":"..actionlocallimit=10-- 最大次数localperiod=10-- 时间窗口(秒)-- 方式1:INCR + EXPIRE(存在问题)redis.call('INCR',key)redis.call('EXPIRE',key,period)-- 方式2:SET + INCR(正确实现,解决竞态条件)-- 使用 SET + EXPIRE 原子操作,避免窗口切换时丢失数据redis.call('SET',key,0,'EX',period,'NX')localcount=redis.call('INCR',key)returncount<=limit

关键点:使用 SET + EXPIRE 代替单独 EXPIRE,避免 INCR 和 EXPIRE 之间进程崩溃导致数据丢失。

可通过 Pipeline 保证两个命令同时发送:

# Python 示例pipe=redis.pipeline()pipe.set(key,0,ex=period,nx=True)pipe.incr(key)res=pipe.execute()returnres[1]<=limit
3.3 固定窗口的局限性

假设 5 分钟内限定 10 次请求:

20:04-20:05 发生 9 次请求 20:05-20:06 发生 9 次请求

在 20:04-20:06 这 2 分钟内,实际发生了 18 次请求,远超每 5 分钟 10 次的限制。

问题根源:固定窗口的边界不连续,在边界处可能发生突发流量。


四、滑动窗口限流

4.1 核心思想

滑动窗口的核心是窗口随时间连续滑动,而非固定边界:

传统固定窗口: |-----5min-----|-----5min-----| 20:00 20:05 20:10 滑动窗口: 现在时刻的窗口持续向前滑动 |----5min-----|----5min-----| 20:01 20:06
4.2 Redis 实现(ZSET)
localfunctionis_action_allowed(red,user_id,action,period,max_count)localkey="***"..user_id..":"..actionlocalnow=redis.call('TIME')-- 获取当前时间戳(毫秒)-- 1. 记录当前行为(score 和 member 都用时间戳)red:zadd(key,now,now)-- 2. 移除窗口之前的行为记录red:zremrangebyscore(key,0,now-period*1000)-- 3. 获取窗口内的行为数量localcount=red:zcard(key)-- 4. 设置过期时间,避免冷用户持续占用内存red:expire(key,period+1)returncount<=max_countend

流程图:

时间轴:[--窗口period--|---未来---] ↑now ZSET 存储:score=时间戳, member=时间戳 ZREMRANGEBYSCORE:删除 score < now-period 的旧记录 ZCARD:统计剩余元素数量,即窗口内请求数
4.3 为什么用 ZSET 而非 LIST
数据结构适用场景
ZSET支持按时间范围删除,适合滑动窗口
LIST只能按索引删除,无法按时间范围清理

五、漏斗限流(Redis-Cell)

5.1 什么是漏斗限流

漏斗限流的核心是容量固定 + 速率固定,能精确控制元素的容量和速率:

漏斗模型: [入口] -> (容量固定) -> [出口] ↓ 速率恒定
  • 漏斗容量:最多能容纳多少请求
  • 漏斗速率:单位时间内能处理多少请求
5.2 Redis-Cell 模块安装

Redis-Cell 是 Redis 的第三方模块,采用 Rust 编写,需要单独安装:

# 下载并编译gitclone https://github.com/brandur/redis-cellcdredis-cellcargobuild--releasecptarget/release/libredis_cell.so /path/to/modules/# 启动 Redis 加载模块redis-server--loadmodule/path/to/modules/libredis_cell.so
5.3 CL.THROTTLE 命令详解
CL.THROTTLE key capacity operations seconds[quota]

参数说明:

参数含义示例
key漏斗容器名称user:123:login
capacity漏斗容量(最大容纳请求数)10
operations单位时间内的操作次数5
seconds单位时间(秒)60
quota单次行为消耗的令牌数(可选,默认1)1

示例:每 60 秒最多 5 次请求,漏斗容量 10

CL.THROTTLE user:123:login10560

返回结果:

1) (integer) 0 # 是否被限流(0=允许,1=拒绝) 2) (integer) 7 # 漏斗剩余容量 3) (integer) 7 # 如果被拒绝,还需要等多久(秒) 4) (integer) -1 # 预留字段 5) (integer) 60 # 下次请求的间隔时间
5.4 流速计算
流速 = operations / seconds = 5 / 60 ≈ 0.083 请求/秒

这意味着每秒只能处理约 0.083 个请求,即约 12 秒处理 1 个请求。


六、令牌桶限流

6.1 核心思想

令牌桶的核心是令牌以固定速率放入桶中

令牌桶: -> [桶容量] -> 请求消耗令牌 -> 通过 ↑ 固定速率放入令牌
  • 桶容量:最大令牌数
  • 令牌添加速率:每秒添加多少令牌
  • 请求消耗:每个请求消耗 1 个令牌
6.2 特点
特点说明
支持突发流量桶满时可一次性处理多个请求
令牌非即时补充需要等待令牌生成
6.3 与漏斗限流的区别
对比维度漏斗限流令牌桶限流
速率匀速匀速(令牌补充)
突发能力不支持支持(桶满时)
实现难度较简单较复杂

七、四种限流方案对比

维度固定窗口滑动窗口漏斗限流令牌桶
实现复杂度
边界突击
突发流量支持不支持不支持不支持支持
精度控制
额外依赖Redis-Cell

八、面试追问 FAQ

问题回答要点
Q: 为什么固定窗口需要 SET + INCR 组合?单独 INCR + EXPIRE 在进程崩溃时可能丢失数据,SET+EXPIRE 原子操作保证一致性
Q: 滑动窗口为什么要设置过期时间为 period+1?避免窗口边界附近过期导致数据丢失,确保跨窗口的请求仍被统计
Q: 漏斗限流和令牌桶限流各适用于什么场景?漏斗:需要精确控制速率的 API 限流;令牌桶:允许突发流量的场景(如秒杀)
Q: Redis-Cell 是原子操作吗?是,CL.THROTTLE 整个命令是原子的,无需担心并发问题
Q: 滑动窗口的 ZSET 会不会无限增长?不会,每次请求都会清理窗口外的旧数据,且有 expire 保证清理

九、相关题目

题目考察点
Redis 固定窗口限流如何保证原子性?SET + INCR + Pipeline
滑动窗口限流为什么用 ZSET 而不是 LIST?按时间范围删除的能力
漏斗限流如何计算流速?operations / seconds
令牌桶和漏斗限流的本质区别?突发流量支持

十、总结

限流方案实现难度精度突发流量推荐场景
固定窗口不支持简单场景
滑动窗口不支持需要精确控制
漏斗限流不支持API 限流
令牌桶支持秒杀/抢购

核心结论:根据业务场景选择合适的限流方案,简单场景用固定窗口,精确控制用滑动窗口或漏斗限流,需要突发能力用令牌桶。


根据零声教育教学写作https://github.com/0voice

http://www.zskr.cn/news/1340303.html

相关文章:

  • 江苏储能电池箱定制企业排行 品质保障实力盘点 - 奔跑123
  • Prometheus标签操作实战:从label_replace到group_left,搞定K8s监控数据关联与聚合
  • 从恶意软件分析到CTF解题:Capstone反汇编框架的5个实战应用场景与Python绑定使用指南
  • AI入门该选什么语言?Python仍是最低成本答案
  • FT62F28X芯片烧录全记录:从FMD IDE固件升级到接线避坑指南
  • GPT-3.5和GPT-4写Prompt,差别到底在哪?
  • 健身房私教管理系统 (三):巧妙利用分步表单,解耦 1+N 模型的双表连续写入
  • 苏州晟雅泰电子:关于铠侠芯片物料THGJFGT0T25BAB8的解析及应用领域
  • 今日算法(构造二叉搜索树)
  • 如何快速清理电脑中的重复图片:AntiDupl智能去重工具完全指南
  • 视觉驱动跨平台UI自动化框架:Midscene.js的技术架构与实现原理
  • 手把手教你用vulkaninfo和ldd命令,精准定位Ubuntu下UE游戏Vulkan启动失败的根本原因
  • 2026电梯物联网大数据机构排行榜高频疑问全解答 - 资讯纵览
  • 临近毕业降AI率保姆级教程:嘎嘎降3分钟,知网AI率5%以下 - 我要发一区
  • 启XX辰-头部安全公司面试提问
  • STM32CubeMX 6.14版本保姆级安装教程:从下载到环境变量配置,一次搞定中文乱码
  • 实战踩坑:用Comparator排序List<Map<String, Object>>时,遇到null值、类型转换怎么办?
  • QGIS数据入库实战:如何将Excel坐标点一键导入PostgreSQL/PostGIS数据库
  • Chrome密码恢复终极指南:如何安全找回所有浏览器保存的密码
  • 3步找回被遗忘的压缩包密码:ArchivePasswordTestTool使用全攻略
  • 一基础验证
  • 从实验室到商业项目:Midjourney皮肤质感渲染的临床级验证报告(N=47位皮肤科医生盲测,真实度提升317%的关键3参数组合)
  • 向量库+RAG+大模型在医疗AI中为何常显不足?揭秘图谱如何重塑医疗知识系统信任度!
  • RT-Thread移植到RA4M2(Cortex-M33)踩坑记:HardFault了别慌,手把手教你解读xPSR/CFSR/HFSR
  • 预付卡闲置变现行业解析,瑞祥商联卡红卡合规回收渠道评测 - 资讯纵览
  • 挪威语语音合成精准度跃迁方案(Nynorsk/Bokmål双引擎适配深度解析)
  • 保姆级教程:在Ubuntu上拆解和重组RK356x的update.img固件包
  • 2026AI论文写作工具实测排行榜!这几款才是真神器
  • 2026年天猫代运营服务商权威排名:从宝尊到汉聪,九家实力公司数据对比 - 资讯纵览
  • 《原神》《崩坏:星穹铁道》语音管线拆解(内部PPT级复现):如何用1套模型支撑23种语言+47个角色声线+实时情绪注入