Nginx变量解析延迟触发,索引与哈希双路径查找,仅NGX_HTTP_VAR_INDEXED变量支持O(1)数组访问;flags决定可写性(NGX_HTTP_VAR_CHANGEABLE)和缓存策略(NGX_HTTP_VAR_NOCACHEABLE),索引必须在postconfiguration阶段获取。

变量不是配置一写就生效的,而是等到真正被用到时才触发解析;索引(index)和哈希(variables_hash)是两套并行查找路径,但只有带 NGX_HTTP_VAR_INDEXED 标志的变量才会进数组索引,否则只能走哈希查。
变量定义时的 flags 决定它能不能被 set 或缓存
你看到的 $arg_xxx、$cookie_xxx 这类变量默认不可修改,因为它们的 flags 里没带 NGX_HTTP_VAR_CHANGEABLE;而你在配置里用 set $my_var "hello"; 定义的变量,Nginx 会自动加上这个 flag,所以能被后续 set 覆盖。
是否缓存值,取决于 NGX_HTTP_VAR_NOCACHEABLE —— 比如 $time_local 就带这个 flag,每次访问都重新调用 get_handler;而 $host 默认不带,首次取完就缓存,后续直接返回。
-
NGX_HTTP_VAR_INDEXED是性能关键:它让变量在运行时通过整数下标(index)从r->variables数组中直接取值,比哈希查找快一个数量级 -
NGX_HTTP_VAR_NOHASH表示这个变量不参与全局哈希表构建,只靠索引访问,常用于模块内部私有变量 - 没加
NGX_HTTP_VAR_INDEXED的变量(比如第三方模块漏配的),即使你调用了ngx_http_get_variable_index(),返回的index也是NGX_ERROR
ngx_http_get_indexed_variable() 只对 indexed 变量有效
这是最常被误用的函数。它的作用非常明确:只查 r->variables 数组里已预分配好位置的变量,不查哈希表,也不做任何 fallback。
如果你传入一个没被标记为 NGX_HTTP_VAR_INDEXED 的变量名(比如自定义的 $my_unindexed),即使它存在于哈希表中,该函数也一定返回 NULL。
- 模块开发中必须先调用
ngx_http_get_variable_index()获取合法index,且该调用必须发生在postconfiguration阶段之后(即ngx_http_variables_init_vars()已执行) -
index值一旦获取,就固定不变;但它对应的r->variables[index]在每次请求中都是独立副本,线程安全 - 不要在 handler 里反复调用
ngx_http_get_variable_index(),它内部会查哈希表——这反而绕过了索引加速,还浪费 CPU
为什么 ngx_http_get_variable() 看似通用却有隐藏开销
这个函数是“兜底方案”:先按 index 查数组,查不到再查哈希表,最后还尝试调用 get_handler 动态生成。它能拿到所有变量,但代价是路径长、分支多、缓存友好性差。
尤其在高并发场景下,如果大量 location 都依赖未索引的变量(比如用 $request_uri 做条件路由但没走 indexed 路径),ngx_http_get_variable() 的哈希查找会成为瓶颈。
- 内置变量如
$remote_addr、$host默认都带NGX_HTTP_VAR_INDEXED,放心用 - 用
set指令定义的变量,Nginx 自动注册进索引数组,无需额外操作 - 第三方模块导出的变量是否 indexed,得看它在
ngx_http_variable_t数组里定义时有没有显式设置flags |= NGX_HTTP_VAR_INDEXED
模块开发中变量索引必须在配置解析完成后获取
很多模块在 create_loc_conf 或 preconfiguration 阶段就急着调用 ngx_http_get_variable_index(),结果返回 NGX_ERROR —— 因为此时 ngx_http_variables_init_vars() 还没跑,全局变量索引数组根本没建好。
正确时机是 postconfiguration 钩子,或者至少确保在 ngx_http_variables_init_vars() 执行之后。
- 错误示范:
ngx_http_get_variable_index(cf, &name)放在ngx_command_t的set回调里 ——cf是配置上下文,但变量系统尚未初始化 - 正确做法:把
index存为模块的loc_conf字段,在postconfiguration阶段统一初始化 - 调试时可打印
r->variables数组长度(r->connection->log),确认目标变量是否已出现在索引位置
真正难搞的从来不是怎么定义变量,而是变量在生命周期里哪一刻被注册、在哪一层被缓存、由谁来决定要不要重算——这些细节藏在 flags 和调用时机里,错一点,性能或行为就偏很远。











