nginx中hash表的设计

10 篇文章 1 订阅

hash表的整体设计要素

先回顾一下hash表的设计的几个要素:

  • hash函数的构造-散列函数
  • 冲突处理方式
  • 装填因子大小的选择。装填因子 a=n / m。其中m为hash表的bucket个数;(n为关键字的个数。装填因子越大,产生hash冲突就严重。)

hash函数构造方法

经典的字符串hash构造算法主要以下几个:

  • BKDRHash
  • APHash
  • DJBHash
  • JSHash
  • RSHash
  • SDBMHash
  • PJWHash
  • ELFHash

nginx中默认的hash算法采用的是BKDR hash算法

/* BKDR算法推导公式, 将任意长度的字符转换成数字 */
#define ngx_hash(key, c)   ((ngx_uint_t) key * 31 + c)

/* 字符转换成小写,字母的大小写相差32 */
#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)


/* 上次hash的结果作为当前key值,其目的是为了扩大key值冲突的间隔,减少冲突 */
ngx_uint_t
ngx_hash_key(u_char *data, size_t len)
{
    ngx_uint_t  i, key;

    key = 0;

    for (i = 0; i < len; i++) {
        key = ngx_hash(key, data[i]);  /* key * 31 + c   test*/
    }

    return key;
}

/* 将字符串小写后,再使用BKDR算法将任意长度字符串映射成整型*/
ngx_uint_t
ngx_hash_key_lc(u_char *data, size_t len)
{
    ngx_uint_t  i, key;

    key = 0;

    for (i = 0; i < len; i++) {
        key = ngx_hash(key, ngx_tolower(data[i]));
    }

    return key;
}

BDKhash算法推理参考:https://blog.csdn.net/wanglx_/article/details/40400693

常见的冲突处理方法

  • 开放定(寻)址法:冲突产生时候,可存放新项的空闲地址可以被同义词和非同义词都使用。(hash值相同的关键词称为同义词)
    • 线性探测法:顺序查看表中下一个单元直到产生一个空闲单元。
      • 容易导致大量元素堆积,降低查找效率。
    • 平方探测法:计算hash值地址增量进行
      • 可以避免堆积问题,缺点是不能探测到散列表上所有单元,但至少能探测到一半单元
    • 再离散法:使用两个hash函数,第一个发生冲突时候,用第二个hash函数计算地址增量
  • 拉链法: 一般拉链法用的比较多,主要插入删除比较方便。
    • 简单的讲就是将hash值一样的同义词用链表连接起来
    • 优点:插入和删除比较方便
    • 缺点是:指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度

nginx当中使用的是连续非空槽存储碰撞元素的方法—>线性探测法:冲突发生时候,顺序查看表的下一个单元找到一个空闲的表单元

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;
    /* 先获取对应key的hash值位置 */
    elt = hash->buckets[key % hash->size];

    if (elt == NULL) {
        return NULL;
    }

    while (elt->value) {
        if (len != (size_t) elt->len) {
            goto next;
        }

        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表的应用场景

nginx实现了两类hash结构,

  • 一类是key中包含通配符的ngx_hash_wildcard_t,这里的通配符指*号。(可以利用正则表达式匹配,主要用来虚拟主机的匹配)
  • 另一类则是key中不包含通配符的ngx_hash_t

以上分析的是nginx中不含通配符的hash表结构,nginx当中hash表的大小是静态固定的,如果数据项大的话,可以改成可变大小的hash表结构

含有带通配符的hash表设计可以参考:https://www.cnblogs.com/chengxuyuancc/p/3782808.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值