关键在于合理建模请求特征、精准分配共享内存并协同限流策略;其内存消耗取决于key粒度、burst、rate及worker数,而非QPS线性增长。

要让 limit_req_zone 在生产环境稳定支撑百万级并发请求,关键不在“堆内存”,而在合理建模请求特征、精准分配共享内存(shared memory)并协同限流策略。Nginx 的限流本质是基于时间窗口的令牌桶(或漏桶变种),其内存消耗与定义的 key 粒度、桶容量(burst)、速率(rate)及 worker 进程数强相关,而非简单线性随 QPS 增长。
一、理解 limit_req_zone 内存开销的核心公式
limit_req_zone 占用的共享内存主要由三部分构成:
- 每个唯一 key 的元数据:约 64 字节(含哈希槽指针、计数器、上一次请求时间戳等);
- 每个 key 对应的令牌桶状态:固定额外约 32 字节(含当前令牌数、last_used 时间等);
- 哈希表预留空间:默认负载因子 0.75,实际分配内存 ≈(预期最大 key 数 ÷ 0.75)× 单槽大小(通常 64 字节)。
例如:limit_req_zone $binary_remote_addr zone=ip:100m rate=10r/s;
→ 100MB 共享内存最多可容纳约 100 × 1024² ÷ 64 ≈ 1.6M 个独立 IP(未计入哈希冗余);若实际活跃 IP 仅 50 万,内存绰绰有余,但若按 $host:$request_uri 维度限流,key 数可能达千万级,100MB 就会迅速耗尽并触发 limit_req 的拒绝行为(503 或延迟排队)。
二、面向百万并发的 key 设计与粒度收敛
百万级 QPS 不等于百万级唯一 key。盲目细化 key 会导致内存爆炸和哈希冲突升高。应分层收敛:
- 优先使用低基数、高复用 key:
$binary_remote_addr(IPv4 约 4 字节,IPv6 16 字节)比$remote_addr更省空间; - 业务关键接口可组合为
$server_name:$request_uri,但需预估 URI 去参后种类(如 /api/order/:id → 归一化为 /api/order/:id); - 避免使用
$args或$http_x_forwarded_for(易被伪造且基数不可控); - 对微服务网关场景,建议按
$upstream_addr+$request_method分流限流,把压力分散到后端实例维度。
三、burst 与 nodelay 的协同配置要点
burst 参数不增加内存占用,但显著影响排队延迟和内存中待处理请求数的瞬时峰值。错误配置会导致大量请求堆积在共享内存队列中,引发超时雪崩:
- 设
burst=200 nodelay:允许突发 200 请求立即通过,不排队,但超出部分直接 503; - 设
burst=200(无 nodelay):突发请求会被平滑延时释放,内存中需暂存排队元数据,对高 P99 延迟敏感; - 百万并发下,推荐
burst≤ 单 worker 接收能力(如 1k–2k),并配合nodelay防止队列阻塞; - 务必设置
limit_req_status 429并记录日志,用于识别真实攻击或客户端重试风暴。
四、生产级调优与可观测性补位
单靠 limit_req_zone 无法覆盖全链路限流,需结合外部机制形成闭环:
- 共享内存大小建议从 64M 起步,通过
nginx -t && nginx -s reload后观察/var/log/nginx/error.log中 “zone is full” 报警; - 用
stub_status或nginx-module-vts暴露limit_req拒绝数、当前 key 数、命中率; - 对秒级突增流量(如秒杀),前置用 Redis + Lua 实现分布式滑动窗口(精度更高),Nginx 层做兜底快速失败;
- worker 进程数匹配 CPU 核心数,避免多进程争抢同一 zone 锁(Nginx 使用自旋锁优化,但高竞争下仍建议 zone 按 worker 分片或使用
limit_req的dry_run模式灰度验证)。

















