滑动窗口必须用ZSet而非普通key,因其需按时间戳排序、范围查询和批量删除过期项;String或Hash无法高效支持;INCR+EXPIRE仅能实现固定窗口,存在临界突增与并发竞态问题。

滑动窗口为什么必须用 ZSet 而不是普通 key
因为滑动窗口要保留「时间戳+请求记录」的有序集合,且需按时间范围动态剔除过期项。Redis 的 ZSet 天然支持按 score(时间戳)排序、范围查询(ZRANGEBYSCORE)和批量删除(ZREMRANGEBYSCORE),而 String 或 Hash 无法高效完成这些操作。
常见错误是用 INCR + 过期时间模拟限流——它只能做固定窗口,无法实现“过去 60 秒内最多 100 次”这种滑动语义,会导致临界点突增流量。
- 每个请求存为一个 member,score 设为当前毫秒时间戳(
microtime(true) * 1000) - 每次检查前先用
ZREMRANGEBYSCORE清理早于「当前时间 - 窗口时长」的旧记录 - 再用
ZCARD获取剩余请求数,决定是否放行 - 注意:score 必须是整数,PHP 中需
(int)(microtime(true) * 1000),否则 ZSet 排序错乱
PHP 实现滑动窗口限流的核心逻辑
不依赖任何框架或 SDK,纯 Redis 原生命令组合。关键在于原子性——虽然 Redis 单命令是原子的,但多命令需用 Lua 脚本避免竞态。PHP 中推荐封装成一个 Lua 脚本调用。
示例脚本(存在 Redis 中,key 为 rate:api:/user/profile:127.0.0.1):
local key = KEYS[1]
local window_ms = tonumber(ARGV[1])
local max_count = tonumber(ARGV[2])
local now_ms = tonumber(ARGV[3])
<p>-- 清理过期成员
redis.call('ZREMRANGEBYSCORE', key, 0, now_ms - window_ms)</p><p>-- 计数并插入新请求
local count = redis.call('ZCARD', key)
if count </p>
PHP 调用时传入:$redis->eval($script, [$key, $window_ms, $max_count, $now_ms], 1)。注意 PEXPIRE 时间要比窗口略长,防止 key 提前消失导致下个请求重建空集合。
IP + 路径 + 用户 ID 多维限流怎么拼 key
key 设计直接决定限流粒度。别硬编码,用可配置的维度组合生成唯一 key:
- 最简场景(按 IP):
'rate:api:' . $request_path . ':' . $ip - 登录用户优先(有 token 或 session):
'rate:api:' . $request_path . ':' . ($user_id ?: $ip) - 强隔离需求(如后台管理接口):
'rate:admin:' . $request_path . ':' . $user_id
注意:路径需标准化,去除查询参数和尾部斜杠,否则 /user?id=1 和 /user?id=2 会被当成两个 key;建议用 parse_url($url, PHP_URL_PATH) 提取后 trim($path, '/')。
Redis key 长度不宜过长,超 1KB 会影响性能;若维度太多(如加设备指纹、App 版本),考虑哈希截断:substr(sha1($key), 0, 12),但需确保哈希冲突概率足够低。
为什么不用 Redis 的 INCR + EXPIRE 组合
因为 INCR 只能做「固定窗口」:比如“每分钟最多 100 次”,窗口从整点开始切分。用户在 00:59 发 100 次,01:00 又发 100 次,实际 2 秒内扛了 200 次——这违背滑动窗口的设计初衷。
更隐蔽的问题是并发:INCR 后判断是否超限,再 EXPIRE,中间可能被其他请求插队。即使加 WATCH,也难保 Lua 外的原子性。
- 固定窗口适合统计报表类场景(如“今日调用量”),不适合保护后端服务
- 滑动窗口真实反映“最近 N 秒”的压力,对突发流量更敏感、更公平
- ZSet 方案实测在 Redis 6.x 上,单 key 每秒可支撑 5k+ 限流判断(含清理),远高于多数业务峰值
真正容易被忽略的是时间精度——PHP 的 microtime(true) 在某些容器或高负载服务器上可能抖动,建议统一用 Redis 的 TIME 命令取服务端时间,或在 Lua 脚本里用 redis.call('TIME') 获取秒+微秒,再换算成毫秒 score。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











