limit_req_zone 限制单位时间内同一 key 的请求频次,非并发连接数;内存消耗取决于活跃限流键数量而非总 IP 数,需通过聚合 key、分层 zone 和跳过低价值流量来优化,如按国家码聚合仅需 10MB。

直接用 limit_req_zone 支持“千万级缓存并发”是个常见误解——它不处理缓存,也不面向“并发连接数”,而是限制**单位时间内同一 key 的请求频次**。所谓“千万级缓存并发”,实际想表达的是:在千万级 IP 或用户量级的业务场景下,如何让限流机制稳定、精准、不崩盘。核心不是堆内存,而是压缩状态维度、分层控制、协同外部系统。
别算“并发连接”,算“活跃限流键数量”
限流内存消耗取决于 同时需要被记住的 key 数量,不是总 IP 数或 QPS。每个 key(如一个 IP、一个用户 ID、一个国家码)在共享内存中占约 64 字节:
- 100 万个活跃 key → 约 64 MB
- 1000 万个活跃 key → 约 640 MB(远超常规 Nginx 实例内存上限)
- 若用
$remote_addr文本形式,还会额外多占 15%~20% 内存
所以第一步是明确:你真正需要长期跟踪的“活跃限流单元”有多少?是全部 IP?还是登录用户?还是异常 UA 组合?答案几乎从来不是“一千万个独立 IP”。
用聚合 key 替代单 IP 记录
把千万级原始输入,映射为几百到几千个高价值逻辑单元:
- 按地域聚合:
key $geoip2_country_code→ 全球 200+ 国家,10MB 就够 - 按网段粗略归并:
key $binary_remote_addr改为key ${remote_addr%.*.*}(需配合 map 指令提取前两段) - 对 CDN 流量:
key "$http_x_forwarded_for.$server_name",把同一源站 + 同一客户视为一个单元 - 对已登录用户:
key $http_x_user_id,真实用户数通常远低于 IP 总量
分 zone 分层,让内存各干各的事
不指望一个 zone 扛全部压力,而是按风险等级和行为粒度拆开:
-
zone=burst_api:5m rate=100r/s:突发接口兜底,只留活跃 IP(60 秒无访问即淘汰) -
zone=login_strict:2m rate=3r/m:登录页严控,key 用$binary_remote_addr$http_user_agent防轮换爆破 -
zone=user_action:8m rate=20r/s:用户行为层,key 是$http_x_user_id,覆盖核心用户群
三者加起来 15MB 内存,却比单个 100MB 的 per-IP zone 更有效、更可控。
跳过低价值流量,释放内存给关键路径
不是所有请求都值得记状态。用 map 指令把确定安全的流量映射为空 key:
- CN2/阿里云回源 IP、健康检查探针、内部服务调用 → key 设为 "",完全不进限流区
- 静态资源路径(
/static/、/healthz)不套limit_req - 只在
/login、/api/submit、/pay等敏感路径上启用限流
这样,内存只服务于真正需要防护的请求,而不是为爬虫、监控、CDN 填充无效槽位。

















