本文及後續文章,redis版本均是v3.2.8上篇文章《redis 資料結構之dict》,我們對dict的結構有了大致的印象。此篇文章對dict是如何維護資料結構的做個詳細的理解。
老規矩還是開啟redis的原始碼,檔案dict.c
一、dict資料結構的維護
1、dictcreate - 建立乙個新的雜湊表
/* reset a hash table already initialized with ht_init().從上述的**中,可以看出dictcreate為dict的資料結構分配空間並為各個變數賦初值。其中兩個雜湊表ht[0]和ht[1]起始都沒有分配空間,table指標都賦為null。這就說明要等第乙個資料插入時才會真正分配空間。* note: this function should only be called by ht_destroy(). */
static void _dictreset(dictht *ht)
ht->table = null;// hash table初始化
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
/* create a new hash table */
dict *dictcreate(dicttype *type,
void *privdataptr)
dict *d = zmalloc(sizeof(*d)); // 分配記憶體
_dictinit(d,type,privdataptr);// dict初始化
return d;
/* initialize the hash table */
int _dictinit(dict *d, dicttype *type,
void *privdataptr)
_dictreset(&d->ht[0]);
_dictreset(&d->ht[1]);
d->type = type;
d->privdata = privdataptr;
d->rehashidx = -1;
d->iterators = 0;
return dict_ok;
2、dictfind - dict查詢
dictentry *dictfind(dict *d, const void *key)_dictrehashstep,可以理解為增量式重雜湊。dictentry *he;
unsigned int h, idx, table;
if (d->ht[0].used + d->ht[1].used == 0) return null; /* dict is empty */
if (dictisrehashing(d)) _dictrehashstep(d);
h = dicthashkey(d, key);
for (table = 0; table <= 1; table++)
/* check if we already rehashed the whole table... */
//ht[0]剩餘元素個數為0,表明ht[0]中的元素已經全部rehash到ht[1]中,因此rehash過程已經完成
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);//可以釋放ht[0],並將ht[1]賦給ht[0]後重置ht[1]
d->ht[0] = d->ht[1];
_dictreset(&d->ht[1]);
d->rehashidx = -1;//表明rehash已經結束
return 0;
/* more to rehash... */
return 1;//否則還處於rehash過程中
dictrehash每次將重雜湊至少向前推進n步(除非不到n步整個重雜湊就結束了),每一步都將ht[0]上某乙個bucket(即乙個dictentry鍊錶)上的每乙個dictentry移動到ht[1]上,它在ht[1]上的新位置根據ht[1]的sizemask進行重新計算。rehashidx記錄了當前尚未遷移(有待遷移)的ht[0]的bucket位置。
如果dictrehash被呼叫的時候,rehashidx指向的bucket裡乙個dictentry也沒有,那麼它就沒有可遷移的資料。這時它嘗試在ht[0].table陣列中不斷向後遍歷,直到找到下乙個存有資料的bucket位置。如果一直找不到,則最多走n*10步,本次重雜湊暫告結束。
最後,如果ht[0]上的資料都遷移到ht[1]上了(即d->ht[0].used == 0),那麼整個重雜湊結束,ht[0]變成ht[1]的內容,而ht[1]重置為空。
對於重雜湊過程的分析,正如上篇文章對dict結構圖中所展示的正是rehashidx=2時的情況,前面兩個bucket(ht[0].table[0]和ht[0].table[1])都已經遷移到ht[1]上去了。
總結rehash操作分為擴充套件和收縮兩種情況,
dict中有兩個hash表,ht[0]和ht[1]。從**中看出,dict的rehash並不是一次性完成的,而是分多次、漸進式的完成的。具體的說dict有兩種不同的策略:
1、_dictrehashstep:所有的資料都是存在放dict的ht[0]中,ht[1]只在rehash的時候使用。dict進行rehash的時候,將ht[0]中的所有資料rehash到ht[1]中。
2、dictrehashmilliseconds:每次執行一段固定的時間,時間到了就暫停rehash操作。
為什麼要rehash?
1、從感性上說,隨著hashtable中的資料增多,衝突的元素增多,ht[0]的鍊錶增長,查詢元素效率就越低,因此就需要rehash。
2、從**角度看,雜湊表利用負載因子loadfactor = used/size來表明hash表當前的儲存情況。當負載因子過大時操作的時間複雜度增大,負載因子過小時說明hash表的填充率很低,浪費記憶體。由於redis中的資料都是儲存在記憶體中的,因此我們必須盡量的節省記憶體。因此我們必須將loadfactor控制在一定的範圍內,同時保證操作的時間複雜度接近o(1)和記憶體盡量被占用。
-eof-
redis資料結構之dict 概要
1.移植使用 void zmalloc size t size void zcalloc size t size void zfree void ptr define random rand define snprintf snprintf s long long timeinmillisecond...
redis資料結構 dict
typedef struct dict dict 雜湊表結構,每個字典中含有兩個這樣的雜湊表結構,存在兩個的原因是因為我們為了控制雜湊表的負載因子在合理的範圍內時,可能需要rehash,rehash採用的 漸進式雜湊 this is our hash table structure.every di...
Redis內部資料結構總結(2)dict
dict是redis乙個非常重要的基礎資料結構,dict用來維護key和value對映關係的資料結構,與很多語言中的map或者dictionary類似。redis的乙個database中所有key到value的對映,就是乙個dict來維護的。另外,當redis中的hash結構資料較多時,hash的底...