Redis+Lua实战:分布式滑动窗口限流算法全解析

目录

  1. 引言:限流的意义与应用场景
  2. 限流算法概览

    • 固定窗口限流
    • 滑动窗口限流
    • 漏桶与令牌桶
  3. 分布式滑动窗口限流的原理

    • 滑动窗口算法思路
    • 分布式实现挑战
    • Redis与Lua结合优势
  4. Redis+Lua实现分布式滑动窗口限流

    • 数据结构设计
    • Lua脚本详解
    • Redis调用方式
  5. 完整代码示例

    • Python示例
    • Node.js示例
  6. 工作流程图解
  7. 性能优化与注意事项
  8. 总结与实践建议

1. 引言:限流的意义与应用场景

在高并发场景下,服务端需要对请求进行限流,以防止系统过载。典型应用场景包括:

  • API接口防刷
  • 秒杀活动限流
  • 微服务调用流量控制

分布式系统中,单点限流容易成为瓶颈,因此采用Redis+Lua实现的分布式滑动窗口限流,成为高性能、高可用的方案。


2. 限流算法概览

2.1 固定窗口限流(Fixed Window)

  • 按固定时间窗口统计请求数量
  • 简单,但存在“临界点超额”的问题
窗口长度:1秒
请求限制:5次
时间段:[0s-1s]
请求次数统计:超过5次则拒绝

2.2 滑动窗口限流(Sliding Window)

  • 按时间连续滑动,统计最近一段时间的请求
  • 精度高,平滑处理请求峰值
  • 实现方式:

    • 精确计数(存储请求时间戳)
    • Redis Sorted Set(ZSET)存储请求时间戳

2.3 漏桶与令牌桶

  • 漏桶:固定出水速度,适合平滑处理请求
  • 令牌桶:以固定速率生成令牌,灵活控制突发请求
本文重点讲解滑动窗口算法。

3. 分布式滑动窗口限流的原理

3.1 滑动窗口算法思路

滑动窗口算法核心:

  1. 记录请求时间戳
  2. 每次请求:

    • 删除超出窗口的旧请求
    • 判断当前窗口内请求数量是否超限
    • 超限则拒绝,否则允许

公式

允许请求数量 = COUNT(时间戳 > 当前时间 - 窗口长度)

3.2 分布式实现挑战

  • 多实例并发请求
  • 原子性操作要求:检查+增加
  • 高并发下操作Redis性能问题

3.3 Redis+Lua结合优势

  • Lua脚本在Redis端执行,保证原子性
  • 减少网络往返次数,提高性能

4. Redis+Lua实现分布式滑动窗口限流

4.1 数据结构设计

使用 Redis Sorted Set (ZSET):

  • key:接口标识 + 用户ID
  • score:请求时间戳(毫秒)
  • value:唯一标识(可用时间戳+随机数)

4.2 Lua脚本详解

-- KEYS[1] : 限流key
-- ARGV[1] : 当前时间戳 (毫秒)
-- ARGV[2] : 窗口长度 (毫秒)
-- ARGV[3] : 最大请求数

local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])

-- 删除超出窗口的旧请求
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)

-- 获取当前窗口请求数量
local count = redis.call('ZCARD', key)

if count >= limit then
    return 0  -- 限流
else
    -- 添加新请求
    redis.call('ZADD', key, now, now .. '-' .. math.random())
    -- 设置过期时间
    redis.call('PEXPIRE', key, window)
    return 1  -- 允许
end

4.3 Redis调用方式

Python调用示例(使用redis-py

import redis
import time

r = redis.Redis(host='localhost', port=6379, db=0)

lua_script = """
-- Lua脚本内容同上
"""

def is_allowed(user_id, limit=5, window=1000):
    key = f"rate_limit:{user_id}"
    now = int(time.time() * 1000)
    return r.eval(lua_script, 1, key, now, window, limit)

for i in range(10):
    if is_allowed("user123"):
        print(f"请求{i}: 允许")
    else:
        print(f"请求{i}: 限流")

Node.js调用示例(使用ioredis

const Redis = require('ioredis');
const redis = new Redis();

const luaScript = `
-- Lua脚本内容同上
`;

async function isAllowed(userId, limit=5, window=1000) {
    const key = `rate_limit:${userId}`;
    const now = Date.now();
    const result = await redis.eval(luaScript, 1, key, now, window, limit);
    return result === 1;
}

(async () => {
    for (let i = 0; i < 10; i++) {
        const allowed = await isAllowed('user123');
        console.log(`请求${i}: ${allowed ? '允许' : '限流'}`);
    }
})();

5. 工作流程图解

+---------------------+
|  用户请求到达服务端  |
+---------------------+
           |
           v
+---------------------+
|  执行Lua脚本(原子)  |
|  - 清理过期请求      |
|  - 判断请求数        |
|  - 添加请求记录      |
+---------------------+
           |
     +-----+-----+
     |           |
     v           v
  允许请求      限流返回
  • Lua脚本保证操作原子性
  • Redis ZSET高效管理时间戳

6. 性能优化与注意事项

  1. 键过期设置:使用PEXPIRE防止ZSET无限增长
  2. ZSET最大长度:可结合ZREMRANGEBYRANK控制极端情况
  3. Lua脚本缓存:避免每次发送脚本,提高性能
  4. 分布式部署:所有实例共享同一个Redis节点/集群

7. 总结与实践建议

  • 滑动窗口比固定窗口更平滑,适合高并发场景
  • Redis+Lua实现保证原子性和性能
  • 分布式系统可横向扩展,限流逻辑一致

实践建议

  1. 精确控制请求速率,结合缓存和数据库保护后端
  2. 监控限流命中率,动态调整参数
  3. Lua脚本可扩展:按接口/用户/IP限流

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日