lua_shared_dict必须在http块中定义且仅一次,大小写敏感、命名受限,重复声明或位置错误会导致启动失败;需合理预估内存避免LRU驱逐或浪费;Lua中须用原子操作防止竞态。

直接在 http 块中定义 lua_shared_dict,就能让所有 worker 进程读写同一块内存区域,实现零延迟、无网络开销的数据同步。关键不在“怎么连”,而在“怎么配、怎么用、怎么避坑”。
必须放在 http 块里,且只能定义一次
这是最常出错的位置问题:
– lua_shared_dict 指令**不允许**出现在 server、location 或任何嵌套块中,否则 Nginx 启动报错:[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 —— 两个请求同时读到 99,都会写入 100,实际已超限;<br>
– ✅ 正确做法:优先使用 <code>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 个)。
理解它的边界:快,但不万能
共享字典适合高频、轻量、单机内协同的场景,需清楚其限制:
– 数据随 Nginx reload 或重启**完全丢失**,不持久;
– 仅限本机所有 worker 共享,**不跨机器**,无法替代 Redis 做集群同步;
– value 不支持函数、闭包、含循环引用的 table;
– 不支持模糊匹配、范围查询、聚合统计等复杂操作;
– 适用典型场景:IP/Token/URI 级限流、热点缓存、黑名单开关、实时请求数统计、滑动窗口时间戳维护。

















