nginx的ngx_hash_t是专为配置解析设计的一次性构建、只读查询哈希表,静态初始化后不可增删改,采用连续内存伪开链与预计算桶大小,实现无锁、零运行时分配的o(1)查找,并通过ngx_hash_wildcard_t两级哈希支持通配符匹配。

Nginx 的 hash 表(ngx_hash_t)不是通用动态容器,而是专为配置解析阶段设计的**一次性构建、只读查询**结构,核心目标是极致内存效率与毫秒级查找——它不追求灵活性,而追求在固定场景下的确定性高性能。
静态初始化,不可增删改
整个 hash 表在配置加载时完成一次性初始化,之后完全冻结:
- 所有 key-value 对必须在调用
ngx_hash_init()前全部提供,以数组形式传入(ngx_hash_key_t *) - 初始化后 无法插入新项、无法删除已有项、无法修改键或值,连桶数量和内存布局都不可变更
- 这种限制换来的是无锁、无内存分配、无运行时冲突处理开销,非常适合 worker 进程只读共享的配置场景
连续内存 + 预计算桶大小
不同于传统链地址法使用指针链表,Nginx 采用“伪开链”:每个桶是一段**连续内存块**,内部元素按顺序排列,用 NULL 指针分隔:
- 初始化时先遍历全部 key,统计每个桶预计承载的元素个数,据此精确分配每块内存大小(
bucket_size参数控制上限) - 所有桶内存从一个大块中连续申请(通常来自
pool),避免频繁小内存分配和碎片 -
ngx_hash_elt_t是变长结构:name[1]紧跟在len和value后,key 字符串直接拼接存储,零额外指针开销
高效查找与对齐优化
查询过程极简,平均接近 O(1):
- 先用哈希函数(如
ngx_hash_key_lc)计算 key 的 hash 值,再对桶总数取模得索引i - 直接跳转到
buckets[i]指向的连续内存起始位置 - 顺序比对每个
ngx_hash_elt_t的len和name(区分大小写或小写归一化),直到匹配或遇到NULL - 所有字段按
sizeof(void*)对齐,提升 CPU 缓存命中率;字符串比较前先比长度,快速剪枝
通配符支持:两级哈希协同
针对 *.example.com 或 www.example.* 类 host 匹配,Nginx 不在主 hash 上硬塞通配逻辑,而是用组合策略:
-
ngx_hash_wildcard_t是对ngx_hash_t的封装,含一个基础 hash 和一个 fallbackvalue - 前置通配(
*.a.b)拆解为["b", "a"]反向建哈希;后置通配(a.b.*)按["a", "b"]正向建哈希 - 实际匹配时先查精确 hash,未命中再查对应通配 hash,实现最大前缀/后缀语义,仍保持 O(1) 主干路径











