OpenResty的lua_shared_dict是跨Worker进程共享内存的核心机制,需在http块中声明、支持原子操作、reload不丢失数据、性能达纳秒级。

在 OpenResty 的多 Worker 进程模型中,Lua 共享字典(shm)是实现跨进程高速缓存共享最直接、最高效的方式。它不走网络、不序列化、不切换上下文,所有操作都在同一块 mmap 映射的内存中完成,原子性由 Nginx 自旋锁保障。
一、必须在 http 块中声明字典
lua_shared_dict 只能在 nginx.conf 的 http 上下文中使用,server 或 location 中写会直接报错:[emerg] "lua_shared_dict" directive is not allowed here。
正确示例:
http {
lua_shared_dict product_cache 100m;
lua_shared_dict rate_limit 5m;
lua_shared_dict block_list 2m;
}注意点:
- 名称只允许字母、数字、下划线,大小写敏感
- 大小单位只能是
k或m(如10m合法,10M或10mb会启动失败) - 最小分配值为 12KB;低于 8KB 会触发 emerg 错误
- 同名字典不可重复定义,多个 include 文件中要避免冲突
二、在 Lua 中安全访问与操作
通过 ngx.shared.DICT_NAME 获取字典对象,所有 API 都是原子的,但使用方式直接影响正确性。
推荐用法:
-
计数类场景(如限流)务必用
incr:避免get + set引发竞态。例如 IP 请求计数:
local dict = ngx.shared.rate_limit
local key = "ip:" .. ngx.var.binary_remote_addr
local newval, err = dict:incr(key, 1, 60) -- 原子自增,TTL=60秒
if not newval then
ngx.log(ngx.ERR, "incr failed: ", err)
end-
set和get适合缓存结构化数据(如 JSON 字符串),value 最大支持 8MB,但建议控制在几 KB 内以减少内存碎片 - key 长度不能超过 255 字节;长标识建议哈希压缩,如
md5(ip .. user_agent) - 不支持存储函数、闭包、线程或循环引用 table
三、理解其生命周期与局限性
共享字典的数据驻留在 master 进程创建的共享内存段中,被所有 worker 进程 mmap 映射。这意味着:
- reload 配置时数据保留(因共享内存段未销毁)
- 完全重启 Nginx(stop + start)后数据清空
- 不跨机器,不持久化,不支持模糊查询或聚合统计
- 适合 IP/Token/URI 级别的单机计数、滑动窗口(需自己维护时间戳)、热点数据缓存
- 不适合需要事务、复杂条件筛选或长期存储的场景
四、性能关键:为什么比 Redis 快两个数量级
一次 incr 耗时约 100ns,而 Redis 的 INCR 通常在 1–10ms 量级。差异来自:
- 零网络 IO:无需 socket 通信
- 零序列化:value 是裸内存字节,非协议编码
- 零上下文切换:所有操作在当前 worker 进程内完成
- 锁粒度精细:基于哈希桶的自旋锁,避免全局竞争
在每秒数万请求的网关场景下,这个差距直接决定系统吞吐上限。

















