Nginx 多 worker 进程下 SSL 会话缓存“不同步”不是 bug,而是配置错误——必须在 http 块顶层启用 shared:SSL:size 类型,builtin、off 或分散在 server 块中的配置均导致各进程独立缓存;shared 通过 POSIX 共享内存(shm_open + mmap)实现多 worker 共址访问,内置原子操作保障线程安全。

直接说结论:Nginx 多 worker 进程下 SSL 会话缓存“不同步”不是 bug,而是配置没用对——必须启用 shared 类型的 zone,且只能在 http 块顶层统一声明。其他方式(builtin、off、分散在 server 块里)都会导致每个进程各管各的,统计自然割裂。
shared 是唯一真正跨进程共享的方式
Linux/macOS 下,ssl_session_cache shared:SSL:10m 会让 master 进程调用 shm_open() 创建 POSIX 共享内存段,所有 worker 通过 mmap() 映射到同一物理内存地址。缓存条目(如 session ID → session ticket 的映射)写入的是同一个哈希表槽位,不是复制或同步,是共址访问。
- Windows 官方二进制不支持 shared,配置会报错,只能退化为 builtin(每个 worker 独立缓存)
- 多个 server 块都写
shared:SSL:10m,Nginx 会自动合并为一个 zone;但若参数不一致(比如有的写 10m、有的写 20m),行为未定义 - 不能把 shared 配置写在某个 server 块里——那样只对该虚拟主机生效,且无法被其他 worker 复用
验证是否真正在复用,别靠猜
缓存命中本身不打日志,容易误判“没生效”。有效验证方式只有两个:
- 用
openssl s_client -connect example.com:443 -reconnect连续请求,输出中出现Reused, SSL handshake has read 0 bytes才代表复用成功 - 在日志或响应头中输出
$ssl_session_reused变量:值为.(点)表示复用,0或空表示新建会话
如果始终是新建会话,优先排查客户端是否禁用 session ID(如某些 Android WebView)、或中间设备(WAF、CDN)剥离了 Session ID 字段。
共享内存大小要算准,溢出比“不同步”更常见
缓存条目是共享的,但空间不够时,新会话会挤掉旧条目,导致命中率骤降——这不是同步问题,是容量不足。
- 估算公式:所需字节数 ≈ 每秒新建会话数 × 平均存活秒数 × 0.5KB
- 例如:QPS 2000、
ssl_session_timeout 10m(600 秒)→ 2000 × 600 × 512 ≈ 614MB;实际建议从 50m 或 100m 起步,再根据监控调整 -
ssl_session_timeout别设太长(如 4 小时),现代客户端重连频繁,短超时反而提升缓存周转率
配合原子操作与锁机制,天然线程安全
Nginx 在 shared zone 内部使用 slab 分配器管理内存,关键字段(如 LRU 头指针、计数器)采用 ngx_atomic_t 类型,支持原子增减和 CAS 操作。
- SSL 会话缓存更新 LRU 链表头指针时,用的是原子指针操作,避免全局锁
- 不需要你加额外锁或插件,只要配置正确,多 worker 并发读写就是安全的
- reload 操作会清空 zone 内容,所以热更新后初期命中率会下降,属正常现象

















