Redis限流算法解析与实战教程

 更新时间:2026年04月15日 10:03:15   作者:涛声依旧-底层原理研究所  
文章对比了多种限流算法(如固定窗口、滑动窗口、令牌桶、漏桶,并详细介绍了RedisCell的使用方式和使用建议,在工程优化方面,文章提供了多个建议,如使用清理、保持一致性、增强可观测性等等等,同时也给出了一些实战场景和限流策略的黄金法则

一、经典限流算法的深度对比与选型建议

算法核心思想适用场景推荐使用场景
固定窗口时间分段统计,每段独立计数对性能要求极高、允许短时突增的简单限流日志上报、非核心接口限流
滑动窗口连续时间区间内动态统计请求需要平滑流量控制、避免“窗口边界突增”问题用户行为监控、支付/下单类高敏感接口
令牌桶以恒定速率生成令牌,请求消耗令牌支持突发流量、灵活控制峰值API 网关、微服务入口、消息推送
漏桶请求进入后按固定速率输出,超出则排队或丢弃下游系统处理能力有限,需绝对平滑数据库写入、文件上传、第三方调用

关键差异总结

维度固定窗口滑动窗口令牌桶漏桶
是否允许突发❌ 否✅ 是(部分)✅ 完全支持⚠️ 可容忍但延迟增加
流量平滑性差(边界突增)极好中等(有突发)最好
内存占用极低(单个 key)高(Zset 存 timestamp)中等(Hash)高(List 队列长度不确定)
实现复杂度高(需 Lua 脚本)中(需定时任务/异步消费)
原子性要求一般高(范围查询+更新)极高(读-算-写闭环)高(队列操作)

选型建议:

  • 若追求极致性能且可接受“59秒+1秒”突发 → 用 固定窗口
  • 若业务对流量平滑性要求严格,如防止刷 单、抢购 → 用 滑动窗口令牌桶
  • 若希望在突发情况下仍能放行一定数量请求,同时长期速率受控 → 优先选择 令牌桶
  • 若下游是数据库/文件系统等慢速资源,必须保证输入速率稳定 → 选 漏桶

二、RedisCell 模块详解:官方推荐的“开箱即用”限流利器

为什么推荐使用 RedisCell?

特性说明
✅ 原生支持4.0+ 版本内置模块,无需额外依赖
✅ 高性能使用 C 编写,减少网络往返和解释成本
✅ 原子性保障CL.THROTTLE 是原子命令,无需手动封装 Lua
✅ 突发容忍支持 max_burst,允许短时间内批量通过
✅ 返回信息丰富返回 [status, remaining_tokens, delay],便于前端/中间件决策

命令详解(结合实例)

# 示例:用户 user123 每分钟最多 15 次请求,突发容量 15,正常速率 1次/秒
CL.THROTTLE user123 15 60 1

返回值解析:

[1, 14, 0]   # 允许,剩余令牌 14,无需等待
[0, 15, 2]   # 拒绝,剩余令牌 15,需等待 2 秒后再试
  • 1: 允许请求
  • 0: 拒绝请求
  • remaining_tokens: 当前可用令牌数(可用于降级提示)
  • delay: 如果拒绝,建议等待多少秒再尝试(单位:秒)

注意:CL.THROTTLEperiod 是以秒为单位,但内部是以毫秒精度计算的,因此即使周期较短(如 1 秒),也能做到精确控制。

如何启用 RedisCell?

确保 Redis 版本 ≥ 4.0;

修改 redis.conf 启用模块:

loadmodule /path/to/redis-cell.so

通常安装 Redis 时会自带该模块(路径可能为 /usr/lib/redis/modules/redis-cell.so);

启动后可通过 MODULE LIST 查看是否加载成功。

使用建议:

  • 不要滥用 max_burst:虽然它提升了用户体验,但可能导致下游瞬间压力激增。
  • 配合熔断机制使用:当 delay 大于阈值(如 >3 秒),应触发熔断或降级策略。
  • 日志埋点:记录被限流的请求,用于分析异常流量来源。

三、工程优化与避坑指南(进阶篇)

1.原子性:必须用 Lua 脚本!

❌ 错误做法(易出并发问题):

GET counter
IF count > limit: RETURN reject
INCR counter

→ 存在“竞态条件”,多个请求可能同时读到 count=14,导致超限。

✅ 正确做法(使用 Lua 脚本):

-- 令牌桶逻辑(简化版)
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2]) -- 令牌生成速度(个/秒)
local now = tonumber(ARGV[3])

local bucket = redis.call('HMGET', key, 'last_time', 'tokens')
local last_time = tonumber(bucket[1]) or 0
local tokens = tonumber(bucket[2]) or capacity

local delta = math.max(0, now - last_time)
local add_tokens = delta * rate
tokens = math.min(capacity, tokens + add_tokens)

if tokens >= 1 then
    tokens = tokens - 1
    redis.call('HMSET', key, 'last_time', now, 'tokens', tokens)
    return 1
else
    return 0
end

使用方式:

EVAL "lua_script" 1 user123 10 1 1700000000

优势:所有操作在一个事务中完成,无中间状态暴露。

2.内存治理:防止“数据堆积”

常见陷阱:

  • 滑动窗口使用 Zset:若不清理过期数据,会导致内存持续增长;
  • 令牌桶使用 Hash:若用户过多,且未设置合理过期时间,也会造成内存泄漏;
  • 漏桶使用 List:如果消费者处理慢,队列无限增长。

解决方案:

结构清理策略
滑动窗口(Zset)定期执行 ZREMRANGEBYSCORE key -inf <now - window_size;
或利用 EXPIRE 设置自动过期(注意:只对 key 有效,不能清除旧元素)
令牌桶(Hash)给每个 key 设置合理的过期时间(如 1 小时),或使用 LRU 策略淘汰不活跃用户
漏桶(List)消费端通过定时任务定期清理空桶或超时桶;也可设置最大长度,超过则丢弃新请求

最佳实践

# 滑动窗口:每分钟清理一次过期时间戳
SCHEDULED JOB:
    ZREMRANGEBYSCORE window_key -inf < (now - 60)

 强烈建议:将限流数据的 TTL 控制在合理范围内(如 1~2 小时),避免内存爆炸。

3.分布式环境下的限流一致性

问题:多个服务节点共享同一份 Redis,但各自缓存本地计数 → 不一致!

✅ 解决方案:

方案说明
✅ 所有计数统一由 Redis 统一维护所有请求都走 Redis,避免本地缓存干扰
✅ 使用 Redis Cluster 保证数据分布一致性确保 key 落在同一个 shard,避免跨节点同步延迟
✅ 限流 key 命名规范统一如 rate_limit:user:123, rate_limit:api:/order/create

切忌:在本地内存做限流计数,除非配合 Redis 作为主源同步。

4.限流策略的可观测性 & 监控

限流不是“黑盒”,必须具备可观测性:

必须采集的关键指标:

指标用途
request_count_per_second整体流量趋势
throttle_rate被限流比例(= 被拒请求数 / 总请求数)
average_delay拒绝后平均等待时间
burst_hit_ratio突发请求占比
top_blocked_keys哪些接口/用户最常被限流

推荐监控方式:

  • 将限流返回结果写入日志(如 OpenTelemetry Trace);
  • 使用 Prometheus + Grafana 可视化限流率;
  • 在网关层集成限流统计器,发送至 Metrics Server。

四、实战场景推荐组合

场景推荐算法实现方式
微服务入口限流(如网关)令牌桶RedisCell + CL.THROTTLE
抢购活动防刷滑动窗口ZREVRANGEBYSCORE + Lua 脚本
第三方服务调用保护漏桶List + BRPOP + 定时任务消费
用户行为分析(防爬虫)固定窗口SETNX + EXPIRE
高频日志上报令牌桶自定义脚本,支持突发容忍

五、总结:构建健壮限流系统的黄金法则

法则说明
🔐 原子性第一任何涉及“查-判-改”的操作,必须用 Lua 脚本
🧹 内存要可控定期清理过期数据,合理设置 TTL
🔄 一致性优先分布式下统一依赖 Redis,禁止本地缓存
📊 可观测性强限流行为必须可监控、可告警、可回溯
🛠️ 善用官方工具优先使用 RedisCell,降低出错概率
🎯 按需选型不是越复杂越好,根据业务特性选择最适合的算法

附录:常用限流命令速查表

功能命令
固定窗口计数SET key value NX EX seconds
滑动窗口统计ZREVRANGEBYSCORE key -inf <timestamp
令牌桶判断EVAL "lua_script"
RedisCell 限流CL.THROTTLE key max_burst count period
删除过期数据ZREMRANGEBYSCORE key -inf <now - window

最终建议:

在生产环境中,优先使用 RedisCell,除非有特殊定制需求;

对于复杂场景,基于 Lua 脚本实现令牌桶/滑动窗口,并加入监控与自动化清理机制;

所有策略都应可配置、可观察、可降级

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Jedis操作Redis实现模拟验证码发送功能

    Jedis操作Redis实现模拟验证码发送功能

    Redis是一个著名的key-value存储系统,也是nosql中的最常见的一种,这篇文章主要给大家介绍Jedis操作Redis实现模拟验证码发送功能,感兴趣的朋友一起看看吧
    2021-09-09
  • Windows系统设置Redis服务使其开机自启动

    Windows系统设置Redis服务使其开机自启动

    Redis是一种键值对数据库,也称为内存数据库,因为它可以将数据存储在内存中,而不是在磁盘上,下面这篇文章主要给大家介绍了关于Windows系统设置Redis服务使其开机自启动的相关资料,需要的朋友可以参考下
    2024-01-01
  • 通过Redis实现Token黑名单机制的具体方案

    通过Redis实现Token黑名单机制的具体方案

    如果没有为Token提供主动失效机制,一旦Token被签发,在过期之前将一直有效,存一些安全隐患,所以本文通过将已失效的Token存储在Redis中,可以确保Token在被主动注销后无法继续使用,需要的朋友可以参考下
    2025-11-11
  • Redisson分布式锁解锁异常问题

    Redisson分布式锁解锁异常问题

    文章主要描述了在使用Redisson进行分布式锁操作时,遇到的`IllegalMonitorStateException`异常,通过分析报错信息,作者发现了问题的根源在于线程尝试解锁但未按顺序执行,作者提出了两种解决方案:在解锁前增加判断,确保线程已获得锁
    2024-12-12
  • Redis服务自动开启、设置密码和闪退问题及解决

    Redis服务自动开启、设置密码和闪退问题及解决

    本文介绍了如何在Windows上设置和配置Redis密码、实现Redis服务自动开启以及解决Redis闪退问题的方法和步骤
    2025-12-12
  • Redis下载部署并加入idea应用的小结

    Redis下载部署并加入idea应用的小结

    这篇文章主要介绍了Redis下载部署并加入idea应用,需要的朋友可以参考下
    2022-10-10
  • Redis删除过期key策略详解

    Redis删除过期key策略详解

    Redis是一款高性能的开源内存数据库,广泛应用于缓存、消息队列、实时分析等场景,在Redis中,我们经常需要删除过期的key,以释放内存空间并保持数据的有效性,本文将为您详细介绍Redis的过期key删除策略,帮助您更好地管理和优化Redis数据库
    2023-10-10
  • 解决Redis分布式锁的误删问题和原子性问题

    解决Redis分布式锁的误删问题和原子性问题

    Redis的分布式锁是通过利用Redis的原子操作和特性来实现的,为了保证数据的一致性和避免冲突,可以使用分布式锁来进行同步控制,本文给大家介绍了如何解决Redis分布式锁的误删问题和原子性问题,需要的朋友可以参考下
    2024-02-02
  • Redis不同数据类型使用场景代码实例

    Redis不同数据类型使用场景代码实例

    这篇文章主要介绍了Redis不同数据类型使用场景代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • 基于Redis位图实现系统用户登录统计

    基于Redis位图实现系统用户登录统计

    这篇文章主要介绍了基于Redis位图实现系统用户登录统计,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11

最新评论