前言

nginx的hash表有几种不同的种类, 不过都是以ngx_hash_t为基础的, ngx_hash_t是最普通的hash表, 冲突采用的是链地址法, 不过这里冲突的元素不是一个链表, 而是一个数组, 为了加快访存速度,这种hash表只用于存储一些静态的信息, 例如所有头部信息, 配置信息等等.

涉及数据结构

/*hash元素数据结构包含key和value*/
typedef struct {
/*hash值*/
void *value;
/*hash表原始key的长度, 即name长度*/
u_short len;
/*name即原始的key值*/
u_char name[1];
} ngx_hash_elt_t; /*普通hash表*/
typedef struct {
/*hash元素*/
ngx_hash_elt_t **buckets;
/*hash表中元素数量*/
ngx_uint_t size;
} ngx_hash_t; /*hash表中key数据结构, 主要用于传递一个hash时使用*/
typedef struct {
/*原始key值*/
ngx_str_t key;
/*hash函数计算过key值*/
ngx_uint_t key_hash;
/*hash表中value值*/
void *value;
} ngx_hash_key_t; typedef ngx_uint_t (*ngx_hash_key_pt) (u_char *data, size_t len); /*普通hash表初始化函数*/
typedef struct {
/*hash表指针*/
ngx_hash_t *hash;
/*未使用*/
ngx_hash_key_pt key; /*hash表中容纳最大元素数量*/
ngx_uint_t max_size;
/*hash表中桶的大小, 即容纳一个元素ngx_hash_elt_t大小*/
ngx_uint_t bucket_size; /*hash表名字*/
char *name;
/*内存池用于固定不变的一些数据结构使用*/
ngx_pool_t *pool;
/*临时的内存池,由于内存池中内存是在内存池销毁时统一释放,因此这里对于临时变量使用*/
ngx_pool_t *temp_pool;
} ngx_hash_init_t;

hash表初始化

初始化hash结构, nginx的hash将内存分为一系列桶, 每个桶内放置一个元素或者是所有冲突的元素, 下面是hash存储的示意图, hash表初始化时, 首先确定每个桶的大小, 其次确定hash表长度, 最后把传进函数中的数据, 按照规则放入hash表中.

                                                         hash表分布示意图

|-----一个桶大小bucket_size-----|-----一个桶大小bucket_size-----|-----一个桶大小bucket_size-----|-----一个桶大小bucket_size-----|........
|--------key_hash%size=0--------|--------key_hash%size=1--------|---------key_hash%size=2-------|--------key_hash%size=3--------|........
|------------------|--------|...|------------------|--------|...|------------------|--------|...|------------------|--------|...|........
0 ngx_hash_elt_t len | 1 ngx_hash_elt_t len | 2 ngx_hash_elt_t len 3 ngx_hash_elt_t len 4........
| | ........
| | ........
取余为0的冲突元素依次放入桶内 取余为1的冲突元素 ........
/*计算桶中元素ngx_hash_elt_t大小, ngx_hash_elt_t结构体长度是可变的, 最后一个成员name[1], C语言惯用的手法, 可以让结构体本身和保存的数据连接在一起, 只是一个
*占位指针, 有时候我们定义为name[0], 因此长度也就是sizeof(value) + sizeof(u_short) + sizeof(name) + len, 但是我们看到下面并不是像我们计算的这样,
*由于sizeof(u_short) + sizeof(name)值肯定小于sizeof(void*), 结构体进行内存对齐时, 以成员最长长度进行对齐, 因此以sizeof(void*)进行对齐, 2代表的是sizeof(u_short).
*/
#define NGX_HASH_ELT_SIZE(name) \
(sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
                          ngx_hash_elt_t分布示意图
|------------------------------|---------------|----------------------|
sizeof(void*) sizeof(u_short) 长度为len的name(name只是一个占位指针)
ngx_int_t
ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
{
u_char *elts;
size_t len;
u_short *test;
ngx_uint_t i, n, key, size, start, bucket_size;
ngx_hash_elt_t *elt, **buckets; for (n = 0; n < nelts; n++) {
/*遍历判断是否有元素长度大于我们事先预估的桶大小, 一个桶的大小需要能够容纳所有的冲突元素*/
if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
{
ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
"could not build the %s, you should "
"increase %s_bucket_size: %i",
hinit->name, hinit->name, hinit->bucket_size);
return NGX_ERROR;
}
} test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);
if (test == NULL) {
return NGX_ERROR;
} /*上面比较时,加了一个指针长度, 这里减去*/
bucket_size = hinit->bucket_size - sizeof(void *); /*预估hash表大小, 这里以ngx_hash_elt_t为最小8字节进行计算*/
start = nelts / (bucket_size / (2 * sizeof(void *)));
start = start ? start : 1; if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {
start = hinit->max_size - 1000;
} for (size = start; size <= hinit->max_size; size++) { ngx_memzero(test, size * sizeof(u_short)); for (n = 0; n < nelts; n++) {
if (names[n].key.data == NULL) {
continue;
} key = names[n].key_hash % size;
/*累加冲突元素的长度*/
test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); /*元素大小大于桶大小, 累加size值, 重新进行分配*/
if (test[key] > (u_short) bucket_size) {
goto next;
}
} goto found; next: continue;
} ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0,
"could not build optimal %s, you should increase "
"either %s_max_size: %i or %s_bucket_size: %i; "
"ignoring %s_bucket_size",
hinit->name, hinit->name, hinit->max_size,
hinit->name, hinit->bucket_size, hinit->name); found: /*重置记录元素位置的临时数组*/
for (i = 0; i < size; i++) {
test[i] = sizeof(void *);
} for (n = 0; n < nelts; n++) {
if (names[n].key.data == NULL) {
continue;
} key = names[n].key_hash % size;
test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
} len = 0; /*计算所有元素总长度*/
for (i = 0; i < size; i++) {
if (test[i] == sizeof(void *)) {
continue;
} /*此处进行了字节对齐, 加快访存速度*/
test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size)); len += test[i];
} /*hash表为size个桶分配内存,保存所有桶的指针*/
if (hinit->hash == NULL) {
hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
+ size * sizeof(ngx_hash_elt_t *));
if (hinit->hash == NULL) {
ngx_free(test);
return NGX_ERROR;
} buckets = (ngx_hash_elt_t **)
((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t)); } else {
buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
if (buckets == NULL) {
ngx_free(test);
return NGX_ERROR;
}
} /*整个hash表分配内存*/
elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
if (elts == NULL) {
ngx_free(test);
return NGX_ERROR;
} elts = ngx_align_ptr(elts, ngx_cacheline_size); for (i = 0; i < size; i++) {
if (test[i] == sizeof(void *)) {
continue;
} buckets[i] = (ngx_hash_elt_t *) elts;
elts += test[i]; } /*重置记录元素位置临时数组*/
for (i = 0; i < size; i++) {
test[i] = 0;
} /*循环遍历将所有元素, 填入hash表中*/
for (n = 0; n < nelts; n++) {
if (names[n].key.data == NULL) {
continue;
} key = names[n].key_hash % size;
elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]); elt->value = names[n].value;
elt->len = (u_short) names[n].key.len; ngx_strlow(elt->name, names[n].key.data, names[n].key.len); test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
} /*hash表中空缺的位置置空*/
for (i = 0; i < size; i++) {
if (buckets[i] == NULL) {
continue;
} elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]); elt->value = NULL;
} ngx_free(test); hinit->hash->buckets = buckets;
hinit->hash->size = size; return NGX_OK;
}

hash表查找

了解了hash表存储结构, 从hash表中查找元素变得简单多了, 首先取出元素对应的桶, 然后依次比较name是否相等, 相等则返回对应值value

void *
ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
{
ngx_uint_t i;
ngx_hash_elt_t *elt; /*取出元素对应的桶位置*/
elt = hash->buckets[key % hash->size]; if (elt == NULL) {
return NULL;
} while (elt->value) {
if (len != (size_t) elt->len) {
goto next;
} /*比较对应的name值*/
for (i = 0; i < len; i++) {
if (name[i] != elt->name[i]) {
goto next;
}
} return elt->value; next: /*桶内依次取值, 存时内存进行了对齐, 取时同样需要对齐*/
elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
sizeof(void *));
continue;
} return NULL;
}

NGINX(三)HASH表的更多相关文章

  1. nginx 哈希表数据结构

    1.哈希表ngx_hash_t的优势和特点 哈希表是一种典型的以空间换取时间的数据结构,在没有冲突的情况下,对任意元素的插入.索引.删除的时间复杂度都是O(1).这样优秀的时间复杂度是通过将元素的ke ...

  2. 6.数组和Hash表

    当显示多条结果时,存储在变量中非常智能,变量类型会自动转换为一个数组. 在下面的例子中,使用GetType()可以看到$a变量已经不是我们常见的string或int类型,而是Object类型,使用-i ...

  3. PHP数组/Hash表的实现/操作、PHP变量内核实现、PHP常量内核实现 - [ PHP内核学习 ]

    catalogue . PHP Hash表 . PHP数组定义 . PHP变量实现 . PHP常量实现 1. PHP Hash表 0x1: 基本概念 哈希表在实践中使用的非常广泛,例如编译器通常会维护 ...

  4. Hash表算法

    出处:http://blog.csdn.net/v_JULY_v 第一部分:Top K 算法详解问题描述百度面试题:    搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的 ...

  5. 深入了解STL中set与hash_set,hash表基础

    一,set和hash_set简介 在STL中,set是以红黑树(RB-Tree)作为底层数据结构的,hash_set是以哈希表(Hash table)作为底层数据结构的.set可以在时间复杂度为O(l ...

  6. 十一、从头到尾彻底解析Hash 表算法

    在研究MonetDB时深入的学习了hash算法,看了作者的文章很有感触,所以转发,希望能够使更多人受益! 十一.从头到尾彻底解析Hash 表算法 作者:July.wuliming.pkuoliver  ...

  7. SQL Server三种表连接原理

    在SQL Server数据库中,查询优化器在处理表连接时,通常会使用一下三种连接方式: 嵌套循环连接(Nested Loop Join) 合并连接 (Merge Join) Hash连接 (Hash ...

  8. 海量路由表能够使用HASH表存储吗-HASH查找和TRIE树查找

    千万别! 非常多人这样说,也包括我. Linux内核早就把HASH路由表去掉了.如今就仅仅剩下TRIE了,只是我还是希望就这两种数据结构展开一些形而上的讨论. 1.hash和trie/radix ha ...

  9. Hash 表详解(哈希表)

    散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列 ...

随机推荐

  1. javascript高级编程笔记06(面相对象2)

    1)  构造函数模式 es中的构造函数可以用来创建特定类型的对象,像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境中,此外,也可以创建自定义的构造函数,从而定义自定义对象类型 ...

  2. 1014: [JSOI2008]火星人prefix - BZOJ

    Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 ...

  3. spoj 42

    简单题   水水~~ /************************************************************************* > Author: x ...

  4. 动态链接库中分配内存引起的问题-- windows已在XX.exe中触发一个断点

    动态链接库中分配内存引起的 本文主要是探讨关于在动态链接库分配的内存在主程序中释放所产生的问题,该问题是我在刚做的PJP工程中所遇到的,由于刚碰到之时感动比较诡异(这也是学识不够所致),所以将它写下来 ...

  5. POJ 3786 Adjacent Bit Counts (DP)

    点我看题目 题意 :给你一串由1和0组成的长度为n的数串a1,a2,a3,a4.....an,定义一个操作为AdjBC(a) = a1*a2+a2*a3+a3*a4+....+an-1*an.输入两个 ...

  6. 学点PYTHON基础的东东--数据结构,算法,设计模式---访问者模式

    说实话,感觉不是特别多,可能没遇到过多场面, 所以对应用场景没感觉吧. 反正,各种模式就是把类的实例传来传去,久而久之,产生了一些规律...:) # 轮子,引擎, 车身这些定义好了都不需要变动 cla ...

  7. UIcollectionView的使用(首页的搭建1)

    今天做一个首页的效果:  首页是用UICollectionView做的.下面我来结合首页的效果介绍一下: 一.创建基类继承自UIViewController 01 创建基类继承自UIViewContr ...

  8. Time.deltaTime 含义和应用

    第一種:使用Time.deltaTime 一秒內從第1個Frame到最後一個Frame所花的時間,所以不管電腦是一秒跑60格或者一秒30格.24格,值都會趨近於一. 就結果而言,deltaTime是為 ...

  9. linux2.6中的工作队列接口 workqueue_struct

    http://blog.csdn.net/sfrysh/article/details/5801786 工作队列接口 工作队列接口是在2.5的开发过程中引入的,用于取代任务队列接口(用于调 度内核任务 ...

  10. 利用CCProxy管理小型企业的上网行为

    本实验以实例方式,从操作条件.背景.需求.以及具体要求的几个部分进行说明. 1. 操作条件: 装有Windows Server 2003系统,安装了代理服务程序的虚拟机一台 2. 背景: 为了提高员工 ...