最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何利用Redis Lua脚本实现黑名单的实时拦截_封装原子性的SISMEMBER判断
时间:2026-07-10 10:44:46 编辑:袖梨 来源:一聚教程网
Redis SISMEMBER命令用于判断指定成员是否为集合的成员,返回1(是)或0(否),key不存在时也返回0;在Lua脚本中须用数字比较(如==1),不可当布尔值使用,以保障原子性。
Redis Lua脚本里怎么写 SISMEMBER 判断并立即返回结果
直接用 redis.call('SISMEMBER', 'blacklist', KEYS[1]) 就行,但必须注意:返回值是整数 1 或 0,不是布尔值。Lua 脚本里别写 if redis.call(...) == true —— 这会永远走 false 分支。
常见错误是把返回值当 Lua 布尔用,导致拦截逻辑失效。正确写法是显式比对数字:
local is_blocked = redis.call('SISMEMBER', 'blacklist', KEYS[1]) == 1if is_blocked then return 1else return 0end
为什么非得用 EVAL 而不能先 GET 再判断
因为网络往返 + 客户端条件判断会破坏原子性:A 请求查到不在黑名单,B 在此时把 A 的 ID 加进去,A 接着就通过了。Lua 脚本在 Redis 单线程内执行,SISMEMBER 和后续动作(比如记录日志、限流计数)能包进同一原子操作里。
使用场景典型如 API 网关层的实时拦截,要求“查+拦”不可分割。如果只是读取状态且允许短暂窗口期,那用客户端逻辑也行,但不符合“实时拦截”需求。
要点:
-
EVAL脚本必须把 key 名作为KEYS[1]传入,不能硬编码,否则无法被 Redis Cluster 路由 - 参数个数要匹配:
EVAL script 1 blacklist_key user_id,第一个数字是 key 的数量 - 脚本长度超过 1KB 会影响 Redis 主从复制带宽,简单拦截逻辑保持在百行内
如何让拦截结果直接用于 Nginx/OpenResty 的 access_by_lua*
OpenResty 的 access_by_lua* 阶段需要明确返回状态码或中断请求,不能只靠 Lua 脚本返回 1/0。得配合 redis.call + ngx.exit 使用:
local redis = require "resty.redis"local red = redis:new()red:set_timeout(100)red:connect("127.0.0.1", 6379)local ok, err = red:eval( "return redis.call('SISMEMBER', KEYS[1], ARGV[1]) == 1 and 1 or 0", 1, "blacklist", ngx.var.arg_uid)if ok == 1 then ngx.exit(403)end
注意这里用了 eval 方法而非 evalsha,首次调用会自动缓存 SHA1;但若脚本变更,需清空 Redis 的脚本缓存(SCRIPT FLUSH),否则旧逻辑仍在运行。
容易踩的坑:
- 没设
set_timeout,超时默认是 60s,会卡住整个请求生命周期 - 没检查
connect返回值,连接失败时eval报错,Nginx 日志里是nil调用错误 -
ARGV[1]是字符串,如果传的是数字型 user_id,Lua 里比较仍安全(Redis 自动转),但建议统一用字符串避免歧义
黑名单 key 设计和过期策略怎么配才不爆内存
别用一个大集合存所有黑名单用户,应该按业务维度拆分,比如 blacklist:api_v1、blacklist:payment。这样既能隔离影响面,又方便单独清理。
如果黑名单项有自然过期时间(如封禁 1 小时),不要依赖 EXPIRE 整个 key —— 集合没法给单个成员设 TTL。正确做法是用 ZSET 存时间戳,再用 ZCOUNT + ZREMRANGEBYSCORE 清理过期项;但若只是静态封禁(永久或手动解封),SET + SISMEMBER 最轻量。
性能提示:
-
SISMEMBER平均时间复杂度 O(1),但最坏是 O(N),当集合元素极多(千万级)且哈希冲突严重时可能变慢,这时应考虑分片(如对 user_id 取模分 16 个 key) - Redis 6+ 启用
io-threads对 EVAL 性能无提升,因为 Lua 脚本强制单线程执行 - 监控
lua_calls和lua_scriptcachemisses指标,高频 miss 说明脚本没复用 SHA1
真正难的不是写对那一行 SISMEMBER,而是确保 key 命名可运维、过期机制可收敛、错误路径不静默失败——这些地方一漏,线上就只能靠日志倒查。
相关文章
- 诛仙世界云若·梦影游仙新时装怎么获得 07-29
- 检疫区最后一站灭鼠者成就如何完成 07-29
- 蚂蚁森林神奇海洋2026年1月26日答案 07-29
- 三角洲行动长弓溪谷2.2日密码是多少 07-29
- html-anything 怎么安装?Codex/Claude Code 本地 HTML 编辑器教程 07-29
- Gardenin新滤镜成就如何解锁 07-29