碼神營地-redis數據結構-字典

Redis數據結構-字典
字典是比較麻煩的一種數據結構,數據結構也相對複雜一些,redis2-x和redis4-x版本該數據結構定義是一致的,本文拿redis4-x來看。
更過關於redis操作和學習教程請進入碼神營地官網:www.icodegod.com

  • 數據結構

    這裏寫圖片描述
    首先看看哈希表節點實體的結構,簡單的由key,v,和一個指向下一個節點的指針next。其中key是void*,這樣定義可以支持任意數據類型的key,v是一個共用體,支持void*,uint64_t,int64_t,和double,這麼做得目的還是爲了區分類型,節省空間。內存結構圖如下:
    這裏寫圖片描述

    v的內存結構:共用8字節內存空間
    這裏寫圖片描述
    第二個結構體是字典類型特定函數結構體,裏面是字典類型特定函數,包括hashFunction哈希函數,keyDup鍵複製函數,valDup值複製函數,keyCompare鍵複製函數,keyDestructor鍵銷燬函數,valDestructor值銷燬函數。dictType結構體類型存在的意義就是爲實現特定類型字典提供便利。
    源碼中哈希表的結構定義和字典的定義:
    這裏寫圖片描述
    dictht是哈希表結構,4部分組成,table是一個指向dicEntry的指針數組,其中每一個元素都是一個dicEntry的結構體,size表示hash表的長度,sizemask大小掩碼用於rehash中,used表示該hash表中已使用長度。
    dict是redis中字典的結構,5部分組成,type是一個dictType結構的指針,privdata是void*的指針,用來保存dictType裏面函數傳遞的私有數據,ht[2]是一個大小爲2的dictht類型的數組,每一個元素都是一個dictht類型的哈希表,其中ht[0]用於字典使用,ht[1]用於rehash使用,用來對ht[0]進行擴容或者縮容處理,作爲後備哈希表,rehashindx標誌rehash進度,當沒有進行rehash的時候,該值爲-1,表示沒有進行過一次在哈希操作,以及字典的迭代器iterators。
    源碼中iterators的結構體:
    這裏寫圖片描述
    d指針指向當前所要操作的字典,index表示下標,table是哈希表,safe用於標誌新表格還是舊錶,entry表示當前字典實體,nextEntry表示下一個字典實體。fingerprint表示指紋標記,作用是避免不安全的迭代器迭代現象。

  • 內存結構
    看完整個字典涉及到的數據結構可以得到整個字典的內存結構圖(圖太難畫,盜用redis設計與實現書中圖):
    這裏寫圖片描述

  • 字典創建
    在源碼中字典創建使用函數dictCreate創建,具體實現如下:
    這裏寫圖片描述
    創建字典的過程分爲兩部分:空間開闢,變量初始化。

    1. 空間開闢。redis源碼中的所以內存開闢都採用封裝的zmalloc進行分配,zmalloc底層封裝了c語言函數malloc來進行實現。
    2. 變量初始化。該過程初始化兩個哈希表數據,指針指向null,hash表格大小爲0,字典開始不參與rehash,賦予rehashidx = -1。
  • 添加元素
    創建完一個字典,要想實現往該字典中添加元素實現步驟如下:
    1. 如果該字典中已經有元素且hash表空間足夠能容下這一個鍵值對,那麼不需要進行hash空間再分配操作:

/* Add an element to the target hash table */
int dictAdd(dict *d, void *key, void *val)
{
    dictEntry *entry = dictAddRaw(d,key,NULL);

    if (!entry) return DICT_ERR;
    dictSetVal(d, entry, val);
    return DICT_OK;
}

從源碼可知,先做了設置key的操作,然後再做設置val操作。

dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing)
{
    int index;
    dictEntry *entry;
    dictht *ht;

    if (dictIsRehashing(d)) _dictRehashStep(d);

    /* Get the index of the new element, or -1 if
     * the element already exists. */
    if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)
        return NULL;

    /* Allocate the memory and store the new entry.
     * Insert the element in top, with the assumption that in a database
     * system it is more likely that recently added entries are accessed
     * more frequently. */
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
    entry = zmalloc(sizeof(*entry));
    entry->next = ht->table[index];
    ht->table[index] = entry;
    ht->used++;

    /* Set the hash entry fields. */
    dictSetKey(d, entry, key);
    return entry;
}

設置key操作中首先會檢查該字典中是否使用了rehash,判斷rehash進行到哪一步了,然後會判斷該key是否已經存在,如果存在則直接返回null,無需再重新創建。如果該key值已經存在則返回null,如果該key不存在則判斷使用哪個哈希表進行新的實體採用頭插法插入,然後設置新的實體的key字段。
設置完key調用設置值函數dictSetVal設置key對應值。因爲設置key對應的值比較簡單,redis對應源碼中將該方法設置爲宏定義:

#define dictSetVal(d, entry, _val_) do { \
    if ((d)->type->valDup) \
        (entry)->v.val = (d)->type->valDup((d)->privdata, _val_); \
    else \
        (entry)->v.val = (_val_); \
	} while(0)

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