必须用lua脚本实现原子操作,因get+incr+expire多命令存在竞态导致超发;lua在redis单线程内执行,确保令牌校验与扣减不被中断。

直接用 Redis + Lua 实现多维度令牌桶限流,核心在于把“维度标识 + 资源标识”作为唯一 key,再通过 Lua 脚本完成令牌生成、校验、扣减三步原子操作。它不依赖外部协调服务,天然支持集群,QPS 轻松过万,是当前生产环境最主流的高性能限流方案。
为什么必须用 Lua 而不是多个 Redis 命令?
因为限流本质是“先算令牌够不够,再扣减”,这两步若拆成 GET + INCR + EXPIRE,高并发下必然出现超发——两个请求同时读到 token=1,都判断为“够”,然后各自扣减,结果变成 -1。Lua 脚本在 Redis 单线程内完整执行,中间不会被打断,彻底消除竞态。
常见错误写法(危险):
- 先用 GET 拿当前 token 数
- 再用 INCRBY 扣减 1
- 再用 EXPIRE 设置过期
正确做法:所有逻辑封装进一个 Lua 脚本,一次 EVAL 调用完成全部动作。
多维度 key 的设计要点
key 必须能精准表达“谁、在什么资源上、按什么规则被限”。推荐格式:ratelimit:{dimension}:{identifier}:{resource}
例如:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- IP 维度限流登录接口:ratelimit:ip:192.168.1.100:/auth/login
- 用户维度限流下单接口:ratelimit:user:U100234:/order/create
- 全局维度限流短信发送:ratelimit:global:sms:send
注意:如果用 Redis Cluster,需加 Hash Tag(如 {ratelimit:ip:192.168.1.100}),确保同一 IP 的所有 key 落在同一 Slot,避免 MOVED 重定向开销。
令牌桶 Lua 脚本关键逻辑
脚本需接收 4 个参数:key、桶容量(capacity)、每秒生成令牌数(rate)、当前毫秒时间戳(now)。核心计算逻辑如下:
- 从 Redis hash 中读取 lastTime 和 currentToken
- 算时间差:timeDiff = now − lastTime
- 新增令牌:newToken = min(capacity, currentToken + floor(timeDiff × rate ÷ 1000))
- 仅当 now > lastTime 时才更新 hash 并设 expire(防时钟回拨)
- 若 newToken > 0,扣减 1 并返回 1;否则返回 0
这个设计支持突发流量(桶有容量缓冲),也支持平滑限速(rate 控制长期 QPS),比固定窗口更贴近真实业务压力模型。
如何对接业务代码(以 Spring Boot 为例)
不建议每个接口手写 Lua 调用。推荐用 AOP + 自定义注解方式封装:
- 定义 @RateLimit(dimensions = {IP, USER}, count = 100, interval = 1, timeUnit = SECONDS)
- AOP 切面自动拼出 Redis key(如从 Request 获取 IP、从 SecurityContext 获取 userId)
- 调用统一的 RedisTemplate.eval() 执行预加载的 Lua 脚本
- 返回 0 则抛出 RateLimitException,由全局异常处理器返回 429
这样业务方法零侵入,改规则只改注解,上线无需动业务逻辑。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










