Nginx共享内存区满时不会报“limit_zone_full”错误,而是静默丢弃新key导致限流异常;需通过debug日志确认“zone is full”,按key数扩容zone(如10万IP需≥16m),并用$limit_req_status监控命中率。

当 Nginx 报出 limit_zone_full(实际应为 limit_req_zone 或 limit_conn_zone 相关的共享内存不足)错误时,它通常不会直接以该字符串出现在 error_log 中,而是表现为类似:
2024/05/10 14:22:33 [error] 12345#0: *6789 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.100, server: example.com, request: "GET /api/v1/data HTTP/1.1", host: "example.com"
但更关键的是——若共享内存区(zone)被完全占满、无法插入新 key(如客户端 IP),Nginx 会静默丢弃新条目,导致限流行为异常(例如本该限速的请求未被限制),而 error_log 默认不记录“zone full”事件本身。真正的线索藏在日志级别与调试信息中。
确认是否真因共享内存不足触发限流异常
Nginx 的 limit_req_zone 和 limit_conn_zone 使用 slab 分配器管理共享内存。当 zone 空间耗尽时,新 key 无法插入,Nginx 会回退到“无状态限流”:对未命中 zone 的请求,按默认策略(如拒绝或延迟)处理,但不会报“zone full”。因此需结合以下方式交叉验证:
- 检查
error_log中是否有高频出现的limiting requests, excess: X.XXX by zone "xxx"—— 若大量请求显示相同excess值(如恒为1.000)且对应 key 高频出现,说明该 key 已存在但 zone 无空间容纳更多 key,是间接信号 - 启用
debug日志级别(临时):error_log /var/log/nginx/error.log debug;(注意:仅用于诊断,生产环境禁用)
重启后观察日志中是否出现类似:
limit_req: zone "mylimit" is full, failed to insert key "192.168.1.100" - 用
nginx -T | grep limit_检查当前 zone 定义,重点关注zone=name:size部分,例如zone=ip:10m
计算并调整 zone 共享内存大小
共享内存实际占用 ≈ key 数 × (sizeof(key) + sizeof(value) + slab 开销)。对 limit_req_zone $binary_remote_addr zone=ip:10m rate=10r/s:
-
$binary_remote_addr固定占 4 字节(IPv4)或 16 字节(IPv6) - 每个 key 对应一个
ngx_slab_page_t结构 + 哈希桶开销,实测每 key 平均消耗约 64–128 字节(取决于 Nginx 版本和编译选项) - 保守估算:10MB zone 最多支持约
10×1024×1024 ÷ 128 ≈ 81,920个独立客户端 - 若业务有 10 万活跃 IP,建议至少设为
zone=ip:16m;若含 IPv6 或需长期缓存(如burst大),再上浮 20%~50%
安全扩容 zone 的操作步骤
共享内存区大小不能热更新,必须重启 Nginx,但可最小化影响:
- 先备份原配置:
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak-$(date +%s) - 修改
limit_req_zone或limit_conn_zone行,增大size,例如从zone=ip:10m改为zone=ip:16m - 执行语法检查:
nginx -t—— 必须通过才可继续 - 平滑重载(非重启):
nginx -s reload
⚠️ 注意:Nginx 会新建共享内存区,旧 zone 仍被旧 worker 进程持有,直到它们自然退出;新请求全部进入新 zone,因此扩容立即生效,无需停服 - 观察 5–10 分钟 error_log 是否不再出现相关 excess 日志,同时用
ss -m | grep nginx或cat /proc/$(pidof nginx)/maps | grep rw-s确认新内存映射已加载
预防性监控与优化建议
避免反复排查,建议落地以下习惯:
- 在
http块中添加全局监控变量:log_format limitlog '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$limit_req_status"';
并在 server/location 中启用:access_log /var/log/nginx/limit.log limitlog;
其中$limit_req_status会输出hit/miss/rejected等,便于统计 key 命中率 - 用 Prometheus + nginx-vts-exporter 或自定义 Lua 脚本定期采集
ngx.shared.DICT(若用 lua-resty-limit-traffic)的get_keys()数量,设置告警阈值(如 >85% zone 使用率) - 对高并发场景,优先使用
$binary_remote_addr(而非$remote_addr)减少 key 长度;必要时用hash指令做前缀截断,例如limit_req_zone $binary_remote_addr$host zone=combined:16m rate=10r/s;
本质上,limit_zone_full 不是标准错误码,而是共享内存资源瓶颈的表现。核心动作就三步:用 debug 日志确认瓶颈、按 key 规模合理扩 zone、通过 access_log + status 变量持续观测。不复杂但容易忽略细节。

















