redis學習-字典

基本說明

  1. 字典,是一種用於保存鍵值對的抽象數據結構。
  2. 字典在redis中的應用相當廣泛,
    1. 比如redis的數據庫就是使用字典來作爲底層實現。
      比如我執行命令:
      set msg “hello world”
      實際上就是保存在數據庫的字典裏面的。
    2. 哈希鍵的底層實現也是字典。

字典的實現

Redis的字典使用哈希表作爲底層實現,一個哈希表裏面有很多哈希表節點,每個哈希表節點就保存了字典中的一個鍵值對。

首先是哈希表節點
typedef struct dictEntry{
    //鍵
    void *key;
    //值
    union{
        void *val;
        uint64_t u64;
        int64_t s64;
    }v;
    //指向下一個哈希表節點,形成一個鏈表
    struct dictEntry *next;
}dictEntry;


  1. 每個哈希表節點都保存一個鍵值對
  2. key表示鍵,而v屬性則保存着鍵值對中的值,其中鍵值對的值可以是一個指針,或者是一個uint64_t整數,又或者是一個int64_t整數(共佔一段最大的內存)。
哈希表結構
typedef struct dictht{
    //哈希表數組
    dictEntry **table;
    
    //哈希表的大小
    unsigned long size;
    
    //哈希表大小掩碼,用於計算索引值,總是等於size-1
    unsigned long sizemask;
	//哈希表已有節點的數量
	unsigned long used;
}dictht;
  1. table屬性是一個數組,每一個指向一個dict.h/dictEntry結構的指針,每個dictEntry結構保存一個鍵值對和一個指向下一個鍵值對的指針。
  2. size記錄了哈希表的大小,即table數組的大小。
  3. used記錄了哈希表目前已有節點的數量
字典結構
typedef struct dictType{
    //計算哈希值的函數
    unsigned int (*hashFuntion)(const void *key);
    
    //複製鍵的函數
    void *(*keyDup)(void *privdata,const void *key);
    
    //複製值的函數
    void *(*valDup)(void *privdata,const void *obj);
    
    //對比鍵的函數
    void (*keyCompare)(void *privdata,const void *key1,const void *key2);
    
    //銷燬鍵的函數
    void (*keyDestructor)(void *privdata,const void *key1,const void *key2);
    
    //銷燬值的函數
    void (*valDestructor)(void *privdata,void *obj);
    
}dictType;

typedef struct dict{
    //類型特定的函數
    dictType *type;
    
    //私有數據
    void *privdata;
    
    //哈希表
    dictht ht[2];
    
    //refresh索引,當refresh不在進行時,值爲-1
    int trehashidx;
}dict;
  1. type屬性是一個指向dictType結構的指針,每一個dictType結構保存了一簇用於操作特定鍵值對的函數,redis會爲用途不同的字典設置不同的類型特定的函數。
  2. privdata屬性則保存了需要傳給那些特定函數的可選參數。
  3. ht保存兩個項的數組,數組中的每個項都是一個dictht哈希表,一般情況下,字典只使用ht[0]哈希表,ht[1]哈希表只會在對ht[0]哈希表進行rehash時使用
  4. rehashidx記錄了rehash的進度,如果目前沒有在進行rehash,那麼它的值爲-1.

哈希算法

  1. 將一個新的鍵值對添加字典裏面,首先是用hash算法計算哈希值和索引值:
    hash = dict->type->hashFunction(k0);
  2. 假設計算出的hash值爲8,那麼程序會繼續使用語句:
    index = hash&dict->ht[0].sizemask = 8 & 3 = 0

refresh操作

  1. 爲字典ht[1]哈希表分配空間,空間大小爲第一個大於等於ht[0].used*2的2^n
  2. 然後將ht[0]上的值都映射到th[1]中,之後釋放ht[0],再把ht[0]指向ht[1],ht[1]指向空白哈希表

漸進式refresh

當refresh時,不併不是一下子轉移的,比如當鍵值對太多的時候,這個時候就需要使用標誌rehashhidx,步驟如下:

  1. 爲ht[0]分配空間,讓字典同時次優ht[0]和ht[1]兩個哈希表
  2. 在字典維持一個索引計數器變量rehashidx,並將它的值設置爲0,表示rehash工作正式開始
  3. 在rehash進行期間,每次對字典執行添加,刪除,查找或者更新操作時,程序除了執行指定的操作以外,還會順帶將rehashidx索引值加一,代表已經轉移到第幾個鍵值對數組了
  4. 全部轉移完成後,rehashidx值變爲-1

在rehash操作的期間,如果有增刪改查,就會先在ht[0]查找,如果沒有再到ht[1]中尋找。

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