$binary_remote_addr 比 $remote_addr 省内存是因为前者存储IPv4地址仅需固定4字节二进制值,后者存储字符串(如"192.168.1.1")需11字节以上并含对齐填充;实测1MB共享内存可存IP数从约1.2万提升至3万,综合节省约60%。

用 $binary_remote_addr 替代 $remote_addr 能实打实节省约 60% 的共享内存占用,不是理论值,是 Nginx 内部结构决定的硬节省。
为什么 $binary_remote_addr 比 $remote_addr 省内存
Nginx 在共享内存中为每个客户端 IP 维护一个状态结构,关键差异在地址存储方式:
-
$remote_addr存的是字符串,比如"192.168.1.1"占 11 字节,加上空终止、对齐填充,实际占 16–24 字节(取决于平台) -
$binary_remote_addr存的是固定 4 字节的二进制值(IPv4),IPv6 是 16 字节,无额外开销 - 共享内存里每个 key 还要存哈希桶指针、计数器、时间戳等元数据,key 本身越小,整体结构越紧凑
实测:1MB 共享内存,用 $remote_addr 大概能存 1.2 万个 IP;换成 $binary_remote_addr 后可存约 3 万个——提升超 150%,综合节省约 60%。
limit_req_zone 配置中必须用 $binary_remote_addr 的场景
以下情况不换就等于白配限流,还容易触发 503 Service Temporarily Unavailable:
- 你启用了
burst,但共享内存很快被撑满,突发请求直接被拒(不是排队,是丢弃) - 你配置了
10m区域却只支撑几万 IP,而业务实际有几十万真实客户端(尤其含大量 NAT 后用户) - 你在日志里看到大量
limiting requests, excess: ... by zone "xxx",但nginx -t没报错,其实是内存碎片+key膨胀导致命中率下降
别信“IP 不多所以无所谓”——NAT 网关、CDN 回源、爬虫池都会让同一个 $binary_remote_addr 对应多个真实用户,反而更需要精准 key。
$binary_remote_addr 的兼容性与常见误用
它不是万能的,用错反而限不住真实客户端:
- 前端有可信代理(如 Cloudflare、自建 LVS)时,
$binary_remote_addr是代理 IP,不是用户真实 IP;此时必须配合$http_x_forwarded_for或$realip_remote_addr(需启用ngx_http_realip_module) -
$binary_remote_addr对 IPv6 支持完好,但如果你混用$remote_addr做日志分析,两者格式不一致会导致统计偏差 - 不要试图用
map把$remote_addr转成二进制——Nginx 不支持运行时编码,map只能做字符串映射,无效
真正省内存的前提是:key 必须从源头就是紧凑二进制,且整个限流链路(包括 realip 设置)保持一致。
验证内存节省效果的最简方法
别猜,直接看 Nginx 自己的统计:
- 加一行
status页面配置:location /nginx_status { stub_status on; } - 压测前访问一次,记下
Active connections和Reading/Writing/Waiting - 用
ab -n 10000 -c 100 http://yourhost/压测后,再刷/nginx_status - 重点看
Requests总数和Request rate是否稳定;若后者断崖下跌,大概率是共享内存区满导致新 key 无法插入
更准的方式是查共享内存使用率:ps aux | grep nginx 看 worker 进程 RSS,或用 gdb attach 后 dump zone 结构——但日常运维,/nginx_status + 压测前后对比已足够定位是否被内存卡脖子。
真正难的不是写对那行 limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s,而是确认 upstream 是否把真实 IP 透传进来、realip 模块是否生效、日志字段是否和限流 key 对得上——这三个点漏一个,前面省下的内存全白费。











