shared dict 是 Nginx 原生共享内存字典,由 master 创建、所有 worker 并发读写,支持原子操作、TTL 过期与零序列化开销,适用于限流、黑白名单等网关同步场景。

在 Nginx 网关场景中,多个 worker 进程默认相互隔离,无法直接共享变量或状态。若需实现限流、会话保持、黑名单缓存等跨进程一致的功能,必须借助进程间通信机制。Nginx 提供的 shared memory dictionary(共享内存字典) 是最轻量、高效且原生支持的方案——它由 master 进程统一创建并维护在共享内存区,所有 worker 可并发读写,无需外部依赖。
什么是 shared dict?它为什么适合网关同步
shared dict 是 Nginx 的内置模块(ngx_http_lua_module 中的 lua_shared_dict 指令定义),本质是一块由操作系统 mmap 映射的、所有 worker 共享的内存区域,内部采用红黑树 + slab 分配器管理键值对。它的关键特性包括:
- 零序列化开销:数据以二进制形式原生存储,Lua 中的 number/string/table(仅限简单嵌套)可直接存取
- 原子操作保障:
incr、get_set、add等方法底层加锁,避免竞态 - 自动过期:每个 key 可设 TTL(秒级),超时后被后台惰性回收,不阻塞请求
- 无网络延迟:相比 Redis 或数据库,毫秒级响应,适用于高频校验场景(如每秒万级令牌桶更新)
如何声明与访问 shared dict
在 nginx.conf 的 http 块中定义字典,大小需预估并留余量(过大浪费内存,过小触发 LRU 驱逐):
http {
lua_shared_dict rate_limit 10m; # 名为 rate_limit,分配 10MB 共享内存
lua_shared_dict block_list 2m; # 黑名单独立字典,便于单独清理
...
}
在 Lua handler 中通过 ngx.shared.DICT_NAME 访问,例如实现 IP 级请求计数:
local dict = ngx.shared.rate_limit
local ip = ngx.var.remote_addr
local key = "ip:" .. ip
local current, err = dict:get(key)
if not current then
dict:set(key, 1, 60) -- 初始值 1,TTL 60 秒
else
local newval, err = dict:incr(key, 1)
if newval > 100 then
return ngx.exit(429) -- 超限返回 429
end
end
实战注意事项与避坑点
shared dict 不是万能存储,使用中需注意边界和限制:
- key 长度限制:最大 255 字节,过长 key 会被截断或报错,建议用哈希(如 md5)压缩长标识
- value 类型限制:仅支持 nil / number / string / boolean / light thread;table 必须满足「无循环引用、无函数、无 userdata」,且深度建议 ≤3 层
-
内存满时行为:写入失败返回
nil, "no memory",应主动判断 err 并降级(如记录日志、走本地计数) - 不支持遍历:没有 keys() 方法,无法枚举全部 key;如需批量操作(如清空某类 key),应约定命名前缀 + 单独维护索引 key
-
master 重启才重置:worker reload 不清空 shared dict,数据持续存在;若需热更新配置,可用带版本号的 key(如
"cfg:v2:timeout")配合定时检查
典型网关场景示例
结合常见需求,给出可直接复用的模式:
-
动态黑白名单:运营后台调用 OpenResty 接口写入
block_list:set("ip:192.168.1.100", true, 3600),网关层每次请求前dict:get("ip:"..ip)判断拦截 -
分布式令牌桶:用
dict:incr("token:"..app_id, -1)尝试扣减,若结果 ≥0 则放行,否则补发dict:set("token:"..app_id, capacity, 1)重置 -
接口健康状态缓存:上游服务异常时,各 worker 同步写
health:set("svc:user", "down", 30),后续请求快速熔断,避免雪崩










