Nginx源码分析之ngx_hash_t

本文详细介绍了Nginx中ngx_hash_t的实现原理,从数组与hash表的关系开始,讨论了hash表的问题及解决方法,重点阐述了Nginx如何利用bucket解决哈希冲突。文章还探讨了数据结构、散列方法,并给出了初始化、测试代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码位置:
nginx/src/core/ngx_hash.h
nginx/src/core/ngx_hash.c

(一)数组与hash表

从查询的角度来看,数组根据索引值的查询速度很快快。
原因在于数组内元素的位置是基于数组起始位置的绝对位置,而且数组的存储空间是连续的,可以根据下标直接操作指针跳转。

虽然数组的查询速度很快,但是数组的索引值必须是数值,这就很讨厌了。 因为很多情况下,索引值并不是数字,而是字符串什么的。比如用名字来索引一个人。

解决这个问题的一个很容易的办法就是给每个人安排一个学号(先不考虑重名的情况),那么,在实际存储时,按照学号为索引值的数组来存储对应的信息;在查询时,只需要知道名字,就可以得到名字对应的学号,根据学号可以直接从数组中取出信息。

这个解决方法中有两个主要部分:

建立从名字到学号的对应关系;
建立以学号为索引值的数组;
从名字到学号的对应关系可以抽象成从字符串到数值的对应关系,这种对应关系,在数学上表示就是f(k)。其中k表示一个字符串(索引关键字),函数f表示从字符串到数值的对应关系,f(k)表示k经过f映射得到的值。

只要有了f(k),那么将f(k)作为数组的下标即可获取k所对应的信息。即:

k——>f(k)——->info[f(k)]

其中,从k到f(k)的映射函数称为哈希函数,数组info[]称为哈希(hash)表。


(二)hash表的问题及解决方法

理想是丰满的,现实是骨感的。hash表在建立时最关键之处在于找到合适的哈希函数,使得:

k与f(k)之间是一一映射的。即,保证给定对于k存在唯一的f(k)与之对应,同时对于f(k)存在唯一的k与之对应。
f(k)的集合是连续的。即,对于数组info[]而言,不存在数组项为空的情况,可以更加充分利用资源。
可惜,满足上述条件的哈希函数非常困难。
现在使用的各种哈希函数基本上只能保证较小概率出现两个不同的k其f(k)相同的情况。
基本不能保证f(k)的集合是连续的。

因为f(k)的集合不是连续的,所以哈希表也被称为散列表,哈希函数也被称为散列函数。
而出现两个k值对应的f(k)相同的情况,称为哈希冲突。

解决哈希冲突常见的办法
出现散列情况表示可能浪费一点资源,这是可以接受的。但是出现冲突表示会发生信息覆盖,这是错误,不能接受。所以,必须解决哈希冲突。

解决哈希冲突的常见的方法有: 1) 开放地址法;2)再哈希法;3)链地址法;


(三)Nginx中hash表

Nginx的哈希表在内存上大概是长这个样子的:
这里写图片描述

根据哈希表的概念可知:哈希表本身就是一个数组,因此,是一块连续的内存空间。
bucket是一个二重指针因为这样当算出当算出的hash值一样时可以解决冲突
这个用来代替链表的数组还有个名字叫hash桶,所以,会在Nginx源码中看到buckets这样的命名。


(四)数据结构

//该结构表示散列表中的槽
typedef struct {
    /*指向用户自定义元素数据*/
    void             *value;
    /*元素关键字的长度*/
    u_short           len;
    /*元素关键字首地址*/
    u_char            name[1];
} ngx_hash_elt_t;

//散列表结构体
typedef struct {
    ngx_hash_elt_t  **buckets;
    //散列表中槽的个数
    ngx_uint_t        size;
} ngx_hash_t;

ngx_hash_t是基本散列表,此外nginx还提供支持通配符的散列表。

//表示前置或者后置通配符
typedef struct {
    //基本散列表
    ngx_hash_t        hash;
    //value指针可以指向用户数据
    void             *value;
} ngx_hash_wildcard_t;

实际使用的时候,即可能精准匹配,也可能前缀/后缀匹配,因此nginx封装了一个包含这三种情况的结构体:

typedef struct {
    //精准匹配的散列表
    ngx_hash_t            hash;
    //查询前置通配符的散列表
    ngx_hash_wildcard_t  *wc_head;
    //查询后置通配符的散列表
    ngx_hash_wildcard_t  *wc_tail;
} ngx_hash_combined_t;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值