根据以往的学习经验, 比如STL中的哈希表, 利用开链法, vector+list作为容器, 当hashtable中的元素总数超过一定数量时, 选择扩充vector.
再比如libevent中的哈希表, 与STL中的哈希表类似, 但比较复杂, 每个bucket中都可能有一个链表,每个链表元素中也可能存在一个链表. 但理解起来都并不复杂.
现在看的Nginx中的哈希表, 则与上面谈到的哈希有很明显的不同之处.
在nginx中, 存储server_name和ngx_http_core_srv_conf_t的映射时用到了hash结构.
配置server_names_hash_max_size可以控制bucket的最大数量,server_names_hash_bucket_size可以控制每个bucket的大小
下图, 就是nginx存储不含通配符的server_name时使用的hash结构:
同时, 每个bucket的大小必须能保证存放至少一个元素.无论这个元素的大小是多少.
好了, 大致的介绍就到这里. 下面是详细的代码分析.
一、 数据结构的定义
1、基本哈希表的元素
每个元素都是 key-value的形式
2、基本哈希表结构
3、支持通配符的哈希表结构
其实也就是多了一个额外的value指针, 当使用ngx_hash_wildcard_t通配符哈希表作为容器元素时,可以使用value指向用户数据。
这里暂时不做多解释
4、该结构也主要用来保存要hash的数据,即键-值对<key,value>
在实际使用中,一般将多个键-值对保存在ngx_hash_key_t结构的数组中,作为参数传给ngx_hash_init()或ngx_hash_wildcard_init()函数
用于表示即将添加到哈希表中的元素
再比如libevent中的哈希表, 与STL中的哈希表类似, 但比较复杂, 每个bucket中都可能有一个链表,每个链表元素中也可能存在一个链表. 但理解起来都并不复杂.
现在看的Nginx中的哈希表, 则与上面谈到的哈希有很明显的不同之处.
在nginx中, 存储server_name和ngx_http_core_srv_conf_t的映射时用到了hash结构.
配置server_names_hash_max_size可以控制bucket的最大数量,server_names_hash_bucket_size可以控制每个bucket的大小
下图, 就是nginx存储不含通配符的server_name时使用的hash结构:
进行解释之前, 需要提一下, nginx中的哈希表一个特别之处在于, 这个hash表是静态只读的,即不能在运行时动态添加新元素的,一切的结构和数据都在配置初始化的时候就已经规划完毕,所以“init”过程的优劣,对运行时查找的性能影响非常大
我们假设哈希表的bucket数量为size , 每个元素的哈希值为 [hash%size], 从上图可以看到, 此哈希表解决冲突的办法类似开链, 但又并不是使用链表来存储具有相同 [hash%size] 值的元素, 而是令具有相同[hash%size]值的元素存放在一块连续的内存中, 以一个NULL指针作为结尾. 上面我们说到了两个可以控制bucket的最大数量和每个bucket的大小的配置, 但并没有说这个哈希表的bucket数量到底是多少. 试想, 当前我们的bucket大小是确定的, 假如冲突太多, 即有多个元素被存放在同一个bucket, 这样肯定会导致某bucket"超载". 那么解决的办法就是扩大bucket的数量, 即size的值(这样不仅能使原来具有相同[hash%size]的元素不再相同, 还能使冲突发生的更稀疏). 看看能不能保证所有存放多个相同 [hash%size]的元素的bucket不"超载". 但是既然我们设定了bucket的最大数量, 一旦size达到这个值还没能满足我们的要求, 那么就返回错误.同时, 每个bucket的大小必须能保证存放至少一个元素.无论这个元素的大小是多少.
好了, 大致的介绍就到这里. 下面是详细的代码分析.
一、 数据结构的定义
1、基本哈希表的元素
每个元素都是 key-value的形式
typedef struct {
void *value; //即为key-value中对应的value
u_short len; //为key-value中key的长度
u_char name[1]; //为key的首地址. 使用长度为1的数组是为了将来申请的len大小的空间是连续的(详细的请搜索 "0或1长度数组")
} ngx_hash_elt_t;
2、基本哈希表结构
typedef struct {
ngx_hash_elt_t **buckets; //即为哈希表
ngx_uint_t size; //哈希表中bucket的个数
} ngx_hash_t;
3、支持通配符的哈希表结构
其实也就是多了一个额外的value指针, 当使用ngx_hash_wildcard_t通配符哈希表作为容器元素时,可以使用value指向用户数据。
这里暂时不做多解释
typedef struct {
ngx_hash_t hash;
void *value;
} ngx_hash_wildcard_t;
4、该结构也主要用来保存要hash的数据,即键-值对<key,value>
在实际使用中,一般将多个键-值对保存在ngx_hash_key_t结构的数组中,作为参数传给ngx_hash_init()或ngx_hash_wildcard_init()函数
用于表示即将添加到哈希表中的元素
typedef struct {
ngx_str_t key;
ngx_uint_t key_hash; //由哈希函数根据key计算出的值. 将来此元素代表的结构体会被插入bucket[key_hash % size]