Nginx中consistent_hash是第三方模块指令,必须单独置于upstream块内,仅支持单变量参数(如$request_uri),与ip_hash或hash指令互斥;未编译模块时配置合法但静默失效。

consistent_hash 指令必须放在 upstream 块内,且不能和 ip_hash / hash 冲突
直接在 upstream 块里写 consistent_hash $request_uri; 是最简路径,但容易忽略两个硬性限制:一是该指令只接受单个变量作为参数,不支持表达式或拼接;二是它和内置的 ip_hash、hash $xxx;(非 consistent 模式)互斥——Nginx 配置校验会直接报错 "ip_hash" directive is duplicate 或类似提示。
常见错误现象是把 consistent_hash 和 hash $uri consistent; 混用,或误以为它是 hash 指令的一个 flag。实际二者属于完全不同的模块:hash ... consistent 来自官方 ngx_http_upstream_hash_module,而 consistent_hash 是第三方模块 ngx_http_upstream_consistent_hash 提供的独立指令。
- 确认模块已加载:运行
nginx -V 2>&1 | grep -o with-http_upstream_consistent_hash,无输出说明未编译进 Nginx - 变量选型优先级:
$request_uri(含参数) >$uri(不含参数) >$args(仅参数),CDN 场景下若参数无关(如 tracking id),务必先用map过滤掉再哈希 - 权重仍生效:每个
server行可带weight=2,模块内部按weight * 160计算虚拟节点数,不是简单取模
为什么 $request_uri 比 $remote_addr 更适合静态资源缓存
静态资源(如 /static/js/app.min.js?v=2.3.1)的缓存价值取决于「相同 URI 是否总落到同一台后端」,而非「同一个用户是否总连同一台」。用 $remote_addr 哈希会导致:同一资源被不同 IP 用户反复请求时,分散打到多台后端,每台都需独立回源、解压、缓存,浪费带宽与 CPU。
而 $request_uri 天然绑定资源粒度,只要 URI 字符串一致(注意大小写、编码、参数顺序),哈希结果就稳定。但要注意:
-
$request_uri包含查询参数,若 CDN 请求带随机utm_*或_t=123类参数,会导致哈希散列失效 —— 必须用map归一化,例如剔除所有_t=参数 - 避免用
$host或$http_referer:前者在多域名共用 upstream 时导致资源重复缓存;后者极不稳定,referer 可能为空或频繁变化 - 小文件场景下,
$uri足够;大文件(如视频分片)建议加$args中关键参数(如range),否则不同 range 请求可能被哈希到不同节点,破坏断点续传逻辑
模块未启用时 nginx -t 不报错,但 reload 后 consistent_hash 不生效
这是最隐蔽的坑:配置语法完全合法,nginx -t 显示 success,reload 也成功,但流量始终走轮询或默认策略。根本原因是模块未编译进二进制,Nginx 遇到不认识的指令会静默跳过,不记录 error log,也不 warn。
验证方式只有两种:
- 执行
nginx -V,检查 configure arguments 是否含--add-module=/path/to/ngx_http_upstream_consistent_hash - 临时在
upstream块里加一行非法配置(如consistent_hash ;缺少变量),再跑nginx -t—— 若仍显示 success,说明该指令根本未注册,模块加载失败 - 线上验证:用 curl 发起多个相同
$request_uri的请求,检查X-Upstream-Addr(需后端透出)或 access log 中 upstream_addr 字段是否固定指向同一台 server
虚拟节点数固定为 weight × 160,增减节点时缓存失效比例可控
模块内部为每个 server 创建 weight × 160 个虚拟节点,均匀分布在哈希环上。这意味着:当集群从 3 台扩到 4 台,原有节点的虚拟节点位置不变,仅新增节点插入新位置,因此只有约 1/4 的 key 会被重映射 —— 对应缓存失效比例就是 ~25%,远低于普通 hash 的 100%。
但这个“可控”有前提:
- 所有 server 的
weight必须显式声明且合理(如性能强的设为 2,弱的设为 1),否则默认 weight=1 会导致高配机器虚拟节点数不足,负载不均 - 不要在运行中动态
down某个 server 后又up它 —— 模块不会重建哈希环,该 server 的虚拟节点位置已偏移,可能导致部分 key 永久错配 - 真实节点 IP+端口变更(如容器漂移)等价于删除旧节点+添加新节点,同样触发局部重哈希,这是设计使然,无需规避
真正要盯住的是哈希环分布是否倾斜:用 nginx -T | grep -A 5 "upstream backend" 看配置,再结合 access log 统计各 upstream_addr 的请求数占比,偏差超过 ±15% 就得检查 weight 设置或变量稳定性。


















