redis源碼分析之字典源碼分析

=====================================================

redis源碼學習系列文章:

redis源碼分析之sha1算法分析

redis源碼分析之字典源碼分析

redis源碼分析之內存編碼分析intset, ziplist編碼分析

redis源碼分析之跳躍表

redis源碼分析之內存淘汰策略的原理分析

redis源碼分析之對象系統源碼分析string, list鏈表,hash哈希,set集合,zset有序集合

=====================================================

在我的github上會持續更新Redis代碼的中文分析,地址送出https://github.com/chensongpoixs/credis_source,共同學習進步

前言

散列表

理解散列存在意義

解決內存使用儘可能少內存 (空間問題)

簡單定義一個散列函數 公式:

hash(k) = k % M == 得到的值 是數組中id 數組中存儲 實際 數據存儲的地址

分析流程

  1. 散列基本原則
  2. redis中的哈希均勻
  3. redis怎麼解決哈希碰撞

正文

一, 散列函數設置原則

  1. 確定性(determinism): 同一關鍵碼總是被映射至同一地址
  2. 快速性(efficiency): expected-O(1)
  3. 滿射性(surjection): 儘可能充分地覆蓋整個散列空間
  4. 均勻性(uniformity): 關鍵碼映射到散列表各位置的概率儘量接近可有效避聚集(clustering)現象

在redis中的哈希因子怎麼確定了使用sha1算法生成160位的的16字節關於sha1算法可以這裏redis源碼分析之sha1算法分析

在redis服務器初始化時hash的因子


	char hashseed[16];
	getRandomHexChars(hashseed, sizeof(hashseed));
	// 設置hash因子
	dictSetHashFunctionSeed((uint8_t*)hashseed);

二, redis中的哈希均勻

在redis使用google研發siphash算法

我們正常使用兩個差別不大字符串經過hash後差別非常大,當時google的siphash算法兩個相差不大的hash值也不大的

siphash的算法我也暫時完全理解大家可以自行百度,下面提供參考

採用例子來詳解simhash的生成規則。simhash的生成劃分爲五個步驟:分詞->hash->加權->合併->降維

算法描述如下:

輸入爲一個N維向量V,比如文本的特徵向量,每個特徵具有一定權重。輸出是一個C位的二進制簽名S。

1)初始化一個C維向量Q爲0,C位的二進制簽名S爲0。
2)對向量V中的每一個特徵,使用傳統的Hash算法計算出一個C位的散列值H。對1<=i<=C,
如果H的第i位爲1,則Q的第i個元素加上該特徵的權重;
否則,Q的第i個元素減去該特徵的權重。
3)如果Q的第i個元素大於0,則S的第i位爲1;否則爲0;
4)返回簽名S。

解釋:名詞
在信息編碼中,兩個合法代碼對應位上編碼不同的位數稱爲碼距,又稱海明距離。
舉例如下:
10101和00110從第一位開始依次有第一位、第四、第五位不同,則海明距離爲3。

編輯
兩個碼字的對應比特取值不同的比特數稱爲這兩個碼字的海明距離。
一個有效編碼集中,任意兩個碼字的海明距離的最小值稱爲該編碼集的海明距離。

三, redis怎麼解決哈希碰撞

在這裏插入圖片描述

在redis中哈希碰撞發生插入當前節點下一個節點連接成鏈表,然後子啊主線程進行異步漸漸移動字典1中數據移動字典2中的

int dictRehash(dict *d, int n) {
	// empty_visits == 1000;
    int empty_visits = n*10; /* Max number of empty buckets to visit. */
	if (!dictIsRehashing(d))
	{
		return 0;
	}

    while(n-- && d->ht[0].used != 0) 
	{
        dictEntry *de, *nextde;

        /* Note that rehashidx can't overflow as we are sure there are more
         * elements because ht[0].used != 0 */
        assert(d->ht[0].size > (unsigned long)d->rehashidx);
		// rehashindx每次主線程更新檢查1000數據中是否沒有數據沒有直接符合,下次在上次索引後面繼續檢查是否有數據有就檢查是否有哈希衝突
        while(d->ht[0].table[d->rehashidx] == NULL) 
		{
            d->rehashidx++;
			if (--empty_visits == 0)
			{
				return 1;
			}
        }
        de = d->ht[0].table[d->rehashidx];
        /* Move all the keys in this bucket from the old to the new hash HT */
		// 看見吧把hashtable1中數據移動hashtable2中了還是
        while(de) 
		{
            uint64_t h;

            nextde = de->next;
            /* Get the index in the new hash table */
            h = dictHashKey(d, de->key) & d->ht[1].sizemask;
            de->next = d->ht[1].table[h];
            d->ht[1].table[h] = de;
            d->ht[0].used--;
            d->ht[1].used++;
            de = nextde;
        }
        d->ht[0].table[d->rehashidx] = NULL;
        d->rehashidx++;
    }
	// 把hashtable2移動hashtable1表 的指針
    /* Check if we already rehashed the whole table... */
    if (d->ht[0].used == 0) 
	{
        zfree(d->ht[0].table);
        d->ht[0] = d->ht[1];
        _dictReset(&d->ht[1]);
        d->rehashidx = -1;
        return 0;
    }

    /* More to rehash... */
    return 1;
}

結語

個人博客地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章