共享内存是Nginx跨worker进程数据同步的核心机制,由master调用mmap()创建并fork继承,通过偏移量访问、原子操作和锁保障安全,支撑限流、缓存元数据、状态统计等功能。

共享内存是 Nginx 实现跨 worker 进程数据同步的核心机制,不是“多个进程各存一份”,而是所有 worker 共用同一块物理内存——master 进程创建,fork 后自动继承,读写零拷贝、毫秒级响应。
共享内存怎么被创建和继承的
Nginx 启动时,master 进程调用 mmap() 配合 MAP_SHARED | MAP_ANONYMOUS 标志分配一块匿名内存区域,不关联任何文件,内容初始化为 0。随后 fork 出多个 worker 进程,由于 Linux 的 copy-on-write(写时复制)机制,这些子进程在只读访问时共享同一物理页;即使后续有写操作,也仅对修改页做私有拷贝,不影响其他 worker 对原始共享区的读取。
关键点在于:
• 地址空间虚拟地址可能不同,但 Nginx 内部统一用相对偏移(offset)定位结构体字段,避免硬编码指针
• 共享内存生命周期由 master 管理,worker 退出不释放,模块卸载前必须确保无活跃访问,否则易触发 core dump
哪些功能依赖共享内存实现
限流、缓存元数据、状态统计等高频跨进程协同场景,都基于共享内存构建:
-
limit_req / limit_conn:令牌桶计数器或连接数存于共享内存,配合
ngx_shmtx_t锁保障写安全,实现全实例统一限流 - proxy_cache / fastcgi_cache:key 索引、过期时间、缓存状态等元信息放共享内存,减少磁盘或文件系统查找次数;实际响应体仍落盘或走独立内存池
-
stub_status:活跃连接数、已接受请求数等指标由各 worker 原子累加到共享区的
ngx_atomic_t变量,无需锁即可安全更新
如何在配置和 Lua 中使用 shared dict
这是面向网关场景最常用、最轻量的共享内存用法,由 ngx_http_lua_module 提供原生支持:
在 http 块中声明:
lua_shared_dict rate_limit 10m;lua_shared_dict block_list 2m;
在 Lua handler 中直接操作:
• key 最长 255 字节,建议对长标识做哈希压缩(如 md5(ip..ua))
• set(key, val, ttl) 支持秒级过期,后台惰性回收,不阻塞请求
• incr(key, delta) 和 get_set(key, newval) 是原子操作,底层自动加锁
• 数据以二进制原生存储,number/string 直接存取,简单 table 可序列化,但嵌套过深会失败
常见踩坑点与规避方式
共享内存高效但敏感,用错容易引发竞态、崩溃或数据不一致:
- 不能存储含指针的结构体——不同 worker 地址空间不一致,指针失效;应改用起始地址 + 偏移量方式访问字段
- 预估容量要留余量:字典过小会触发 LRU 驱逐,导致有效 key 被误删;过大则浪费内存,且不支持动态扩容
- 模块自定义共享内存时,
ngx_shared_memory_add()只是登记,真正分配在 init_module 阶段;需检查shm_zone->data是否非空再使用 - 写操作必须显式同步:要么用
ngx_shmtx_lock/unlock(),要么用ngx_atomic_fetch_add()等原子指令,裸写变量必然出错











