lua_shared_dict必须在http块中定义且仅一次,大小写敏感、命名受限;需合理预估内存避免LRU驱逐或浪费;Lua中须用incr等原子操作防止竞态,数据非持久、不跨机、不支持复杂类型。

直接在 Nginx 的 http 块中定义 lua_shared_dict,就能让所有 worker 进程读写同一块内存区域,实现零延迟、无网络开销的数据同步。关键不在“怎么连”,而在“怎么配、怎么用、怎么避坑”。
必须在 http 块中声明,且仅一次
这是最常出错的位置问题:
-
lua_shared_dict指令不允许出现在server、location或任何嵌套块中,否则启动报错:[emerg] "lua_shared_dict" directive is not allowed here; - 字典名大小写敏感,且只能含字母、数字、下划线(例如
rate_limit和Rate_Limit是两个不同字典); - 同一名字典若在多个
include文件中重复声明,也会导致启动失败(duplicate "lua_shared_dict" directive)。
正确写法示例:
http {
lua_shared_dict my_cache 10m;
lua_shared_dict block_list 2m;
lua_shared_dict counter 5m;
}
合理预估内存大小,避免 LRU 驱逐或浪费
共享内存不是越大越好,也不是越小越省:
- 过小:键值对频繁触发 LRU 淘汰,有效数据被误清,缓存命中率骤降;
- 过大:占用过多驻留内存,尤其在高并发多字典场景下易引发系统压力;
- 单 key 最长 255 字节,长标识(如带 UA 的 IP)建议先哈希(
md5(ip..ua))再存; - 可按预期 key 数 ×(平均 key_len + value_len + 开销)粗略估算,再上浮 20%~30% 作为余量。
Lua 中必须使用原子操作,禁用 get+set 组合
多 worker 并发下,非原子操作必然引发竞态:
- ❌ 错误示范:
val = dict:get(k); if val < 100 then dict:set(k, val + 1) end—— 两个请求同时读到 99,都会写入 100,实际已超限; - ✅ 正确做法:优先使用
incr、add、get_set等内置原子方法; - 例如限流计数:
local curr, err = dict:incr("ip:"..ngx.var.remote_addr, 1),首次不存在则自动初始化为 1; - 设置带 TTL 的值用
set(key, val, exptime),单位为秒(如3600表示 1 小时); - 过期是惰性清理,访问时才检查,也可主动调用
dict:flush_expired(2)批量清理(最多清 2 个)。
清楚 shared dict 的能力边界
它快,但不万能:
- 数据随 Nginx
reload或重启完全丢失,不持久; - 仅限本机所有 worker 共享,不跨机器,无法替代 Redis 做集群同步;
- value 不支持函数、闭包、含循环引用的 table;
- 适合高频、轻量、单机内协同的场景,比如限流、黑白名单、短时效缓存等。

















