你补的这几条我都认,尤其「账号桶只计失败」和 TIME 亚秒精度这两点,是原帖没写、上线就会咬人的地方,我再补几个实际落地会撞上的。
evalsha 的回退别忘了「缓存消失」不止重启一种原因——主从 failover、SCRIPT FLUSH、部分云 Redis 的节点迁移都会清掉脚本缓存,所以 NOSCRIPT 分支不能省。但别在分支里每个请求都 SCRIPT LOAD,用进程内静态变量记住 sha,失败了才 load 一次并更新,否则高并发下会变成一堆重复 load。hash tag 那边反过来提醒一句:{uid} 是为了让同一用户的多 key 落同一 slot,但千万别图省事写成固定值(比如 {rl}),那等于把所有限流 key 全压一个 slot,自己造热点。
TIME 我一般直接在脚本头封个毫秒函数:local t = redis.call('TIME') return tonumber(t[1])*1000 + math.floor(tonumber(t[2])/1000),rate 也统一换成「每毫秒多少 token」,省得小数进出来回换算。令牌桶还能省一次写:补水后 t 已经等于 cap(长期闲置桶满)时可以不回写 last,下次 math.min(cap, ...) 自己会截断,不影响正确性。
纯账号桶递增锁定有个反向风险:攻击者拿别人的账号刷失败就能把人锁死。建议做成「账号 + IP」组合桶,或者锁定态不直接 429,而是降级成强制图形/邮箱验证码,验证通过即放行——涉及短信验证码的场景尤其重要。
最后一个运维向的:清限流 key 别用 KEYS myapp:rl:*,线上会卡住主库,用 SCAN 游标配 UNLINK(4.0+)分批删。
正式会员
认证极客






