核心是将黑名单加载到Nginx共享内存字典中实现毫秒级匹配;通过lua_shared_dict声明50MB共享字典,批量初始化后跨worker共享,避免请求级变量和普通Lua表。

核心在于避免每次请求都查文件或远程服务,把黑名单放进内存,并用合适的数据结构快速匹配。
用共享内存字典存 IP 列表
Nginx 的 lua_shared_dict 是关键。它在 worker 进程间共享、纯内存存储、无锁读取,单次查询通常低于 0.1ms。
- 在
http块中声明:lua_shared_dict ip_blacklist 50m;(50MB 可存约 100 万个 IPv4 地址) - 初始化时从 Redis 或本地文件批量加载一次,后续只更新内存字典
- 不建议用
ngx.var或普通 Lua 表,它们是请求级或进程级的,无法跨 worker 共享且易被 GC 清理
选对 IP 匹配数据结构
直接存原始 IP 字符串做 key 是最简单的方式,适合中小规模(192.168.1.0/24),需升级结构:
- 用 CIDR 前缀树(Trie)实现网段匹配:OpenResty 社区有成熟 Lua 库
cidrmatch,支持 O(log n) 查找 - 布隆过滤器可作“快速预筛”:先过一遍布隆过滤器判断“大概率不在黑名单”,跳过后续精确匹配,降低 30%+ 内存访问压力
- 避免用线性遍历——哪怕只有 1000 条规则,平均也要查 500 次,延迟不可控
减少 Redis 查询频次与依赖
动态黑名单常依赖 Redis 同步,但频繁查 Redis 会拖慢响应。优化重点是“懒加载 + 定期刷新”:
- Lua 脚本中不每次请求都连 Redis,而是检查内存中 last_update 时间戳,仅当超过阈值(如 30 秒)才拉取新数据
- 用 Redis 的
PUB/SUB或定时任务触发全量同步,而非轮询 - Redis 连接复用:在
init_by_lua_block中创建并复用连接对象,避免每次新建 socket
冷热分离与失效控制
不是所有黑名单都需要永久存在。区分临时封禁(如 10 分钟 CC 攻击源)和长期恶意 IP,能显著减小内存占用:
- 短期 IP 存入带 TTL 的 Redis key,由后台定时清理;长期 IP 才写入共享字典
- 共享字典中为每个 IP 记录过期时间戳(如
red:set("1.2.3.4", "1720544520")),查询时比对当前时间 - 定期用后台 Lua 脚本扫描字典,剔除过期项,防止内存缓慢增长


















