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