
直接用 ZADD + ZREMRANGEBYSCORE + ZCARD 三步就能实现,但必须用 Lua 脚本原子执行,否则并发下计数会漂移——这是最常被忽略的致命点。
为什么单条命令组合不行?
限流本质是「先删旧、再查数、最后加新」三个动作。如果拆成三次 Redis 请求:
- 两个并发请求同时执行
ZCARD,都读到 99,都判断“还能进” - 接着都执行
ZADD,结果变成 101 —— 超限了却没拦住 - 更糟的是,
ZREMRANGEBYSCORE如果放在ZCARD之后,可能刚删完旧数据,另一个请求就插进来写入,导致窗口统计错位
必须用 Lua 脚本保证原子性
把整个逻辑封装进一个 Lua 脚本,由 Redis 串行执行。例如限流 key 为 limit:ip:{ip},60 秒窗口、100 次上限:
local key = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2]) -- 60
local max = tonumber(ARGV[3]) -- 100
<p>-- 删除窗口外数据
redis.call('ZREMRANGEBYSCORE', key, 0, now - window <em> 1000)
-- 计数
local count = redis.call('ZCARD', key)
-- 如果未超限,添加当前时间戳(用毫秒避免重复)
if count 2) -- 过期设为窗口两倍,防冷 key 残留
end
return count </em></p><p>调用时传入:<code>KEYS = ["limit:ip:192.168.1.1"]</code>,<code>ARGV = ["1725513420123", "60", "100"]</code></p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2199" title="Redis 8.2.3"><img
src="https://img.php.cn/upload/manual/001/589/237/69f31fe50f56d421.png" alt="Redis 8.2.3" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2199" title="Redis 8.2.3" class="overflowclass">Redis 8.2.3</a>
<p class="overflowclass">Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2199" title="Redis 8.2.3" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h3>IP 维度 key 设计与内存控制</h3><p>key 必须带 IP,但不能裸用 <code>limit:ip:{ip}</code> —— IPv6 地址含冒号,Redis key 不支持;且公网 IP 可能被 NAT 共享,需结合 User-Agent 或 token 做二次区分:</p>
- 推荐 key 格式:
limit:ip_v4:<code>sha256(ip),或对 IPv6 做规范化(如压缩+base32) -
EXPIRE时间建议设为window * 2,不是window:避免刚过期就新请求进来,触发重建开销 - 不手动
DEL空 key:ZCARD返回 0 后,key 自然随过期消失,无需额外清理
精度取舍:用秒级还是毫秒级 score?
score 用秒还是毫秒,取决于你的容忍误差和存储压力:
- 用秒(
time()):每秒最多存 1 个 member,内存省,但同一秒内第 101 次请求会被误拒 - 用毫秒(
System.currentTimeMillis()):精度高,但极端场景(如压测)可能在 1ms 内涌入大量请求,member 数暴涨 - 折中方案:score 用毫秒,member 用
requestId或ip:ts:rand,避免 score 相同导致覆盖
真正难的不是写对脚本,而是确认业务能否接受「窗口内统计延迟 100ms」或「冷 key 多占 2 分钟内存」——这些细节不提前对齐,上线后就会卡在监控告警里反复调参。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










