您好,登錄后才能下訂單哦!
這篇文章主要介紹redis中hash如何實(shí)現(xiàn)的,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
redis是KV型的內(nèi)存數(shù)據(jù)庫(kù), 數(shù)據(jù)庫(kù)存儲(chǔ)的核心就是Hash表, 我們執(zhí)行select命令選擇一個(gè)存儲(chǔ)的db之后, 所有的操作都是以hash表為基礎(chǔ)的, 下面會(huì)分析下redis的hash數(shù)據(jù)結(jié)構(gòu)和實(shí)現(xiàn).
/*Hash表一個(gè)節(jié)點(diǎn)包含Key,Value數(shù)據(jù)對(duì) */ typedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; /* 指向下一個(gè)節(jié)點(diǎn), 鏈接表的方式解決Hash沖突 */ } dictEntry; /* 存儲(chǔ)不同數(shù)據(jù)類(lèi)型對(duì)應(yīng)不同操作的回調(diào)函數(shù) */ typedef struct dictType { unsigned int (*hashFunction)(const void *key); void *(*keyDup)(void *privdata, const void *key); void *(*valDup)(void *privdata, const void *obj); int (*keyCompare)(void *privdata, const void *key1, const void *key2); void (*keyDestructor)(void *privdata, void *key); void (*valDestructor)(void *privdata, void *obj); } dictType; typedef struct dictht { dictEntry **table; /* dictEntry*數(shù)組,Hash表 */ unsigned long size; /* Hash表總大小 */ unsigned long sizemask; /* 計(jì)算在table中索引的掩碼, 值是size-1 */ unsigned long used; /* Hash表已使用的大小 */ } dictht; typedef struct dict { dictType *type; void *privdata; dictht ht[2]; /* 兩個(gè)hash表,rehash時(shí)使用*/ long rehashidx; /* rehash的索引, -1表示沒(méi)有進(jìn)行rehash */ int iterators; /* */ } dict;
dict中ht[2]中有兩個(gè)hash表, 我們第一次存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)時(shí), ht[0]會(huì)創(chuàng)建一個(gè)最小為4的hash表, 一旦ht[0]中的size和used相等, 則dict中會(huì)在ht[1]創(chuàng)建一個(gè)size*2大小的hash表, 此時(shí)并不會(huì)直接將ht[0]中的數(shù)據(jù)copy進(jìn)ht[0]中, 執(zhí)行的是漸進(jìn)式rehash, 即在以后的操作(find, set, get等)中慢慢的copy進(jìn)去, 以后新添加的元素會(huì)添加進(jìn)ht[0], 因此在ht[1]被占滿(mǎn)的時(shí)候定能確保ht[0]中所有的數(shù)據(jù)全部copy到ht[1]中.
創(chuàng)建hash表過(guò)程非常簡(jiǎn)單,直接調(diào)用dictCreate函數(shù), 分配一塊內(nèi)存,初始化中間變量即可.
dict *dictCreate(dictType *type, void *privDataPtr) { /*分配內(nèi)存*/ dict *d = zmalloc(sizeof(*d)); /*初始化操作*/ _dictInit(d,type,privDataPtr); return d; }
hash表中添加元素,首先判斷空間是否足夠, 然后計(jì)算key對(duì)應(yīng)的hash值, 然后將需要添加的key和value放入表中.
int dictAdd(dict *d, void *key, void *val) { /*添加入hash表中, 返回新添加元素的實(shí)體結(jié)構(gòu)體*/ dictEntry *entry = dictAddRaw(d,key); if (!entry) return DICT_ERR; /*元素val值放入元素實(shí)體結(jié)構(gòu)中*/ dictSetVal(d, entry, val); return DICT_OK; } /* *添加元素實(shí)體函數(shù) */ dictEntry *dictAddRaw(dict *d, void *key) { int index; dictEntry *entry; dictht *ht; if (dictIsRehashing(d)) _dictRehashStep(d); /*根據(jù)key值計(jì)算新元素在hash表中的索引, 返回-1則表示元素已存在, 直接返回NULL*/ if ((index = _dictKeyIndex(d, key)) == -1) return NULL; /*如果在進(jìn)行rehash過(guò)程,則新元素添加到ht[1]中, 否則添加到ht[0]中 */ ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; entry = zmalloc(sizeof(*entry)); entry->next = ht->table[index]; ht->table[index] = entry; ht->used++; /*設(shè)置元素key*/ dictSetKey(d, entry, key); return entry; } /* *計(jì)算索引的函數(shù) */ static int _dictKeyIndex(dict *d, const void *key) { unsigned int h, idx, table; dictEntry *he; /* 判斷hash表是否空間足夠, 不足則需要擴(kuò)展 */ if (_dictExpandIfNeeded(d) == DICT_ERR) return -1; /* 計(jì)算key對(duì)應(yīng)的hash值 */ h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { /*計(jì)算索引*/ idx = h & d->ht[table].sizemask; /*遍歷沖突列表, 判斷需要查找的key是否已經(jīng)在沖突列表中*/ he = d->ht[table].table[idx]; while(he) { if (dictCompareKeys(d, key, he->key)) return -1; he = he->next; } if (!dictIsRehashing(d)) break; } return idx; } /* *判斷hash表是否需要擴(kuò)展空間 */ static int _dictExpandIfNeeded(dict *d) { /*redis的rehash采用的漸進(jìn)式hash, rehash時(shí)分配了原來(lái)兩倍的內(nèi)存空間, 在rehash階段空間必定夠用*/ if (dictIsRehashing(d)) return DICT_OK; /* hash表是空的需要初始化空間, 默認(rèn)是4*/ if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); /* 已使用空間滿(mǎn)足不了設(shè)置的條件*/ if (d->ht[0].used >= d->ht[0].size && (dict_can_resize || d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)) { /*擴(kuò)展空間, 使用空間的兩倍*/ return dictExpand(d, d->ht[0].used*2); } return DICT_OK; } /* *擴(kuò)展空間或者初始化hash表空間 */ int dictExpand(dict *d, unsigned long size) { dictht n; /* 對(duì)需要分配大小圓整為2的倍數(shù) */ unsigned long realsize = _dictNextPower(size); /* 如果空間足夠則表明調(diào)用錯(cuò)誤 */ if (dictIsRehashing(d) || d->ht[0].used > size) return DICT_ERR; n.size = realsize; n.sizemask = realsize-1; n.table = zcalloc(realsize*sizeof(dictEntry*)); n.used = 0; /*hash表為空初始化hash表*/ if (d->ht[0].table == NULL) { d->ht[0] = n; return DICT_OK; } /*新分配的空間放入ht[1], 后面一步一步進(jìn)行rehash*/ d->ht[1] = n; d->rehashidx = 0; return DICT_OK; }
查找元素過(guò)程,首先計(jì)算hash值, 然后計(jì)算在ht[0]和ht[1]中索引位置, 進(jìn)行查找.
dictEntry *dictFind(dict *d, const void *key) { dictEntry *he; unsigned int h, idx, table; if (d->ht[0].size == 0) return NULL; /*如果正在進(jìn)行rehash, 執(zhí)行一次rehash*/ if (dictIsRehashing(d)) _dictRehashStep(d); h = dictHashKey(d, key); /*由于可能正在rehash, 因此要從ht[0]和ht[1]中分別進(jìn)行查找, 找不到返回NULL*/ for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx]; /*遍歷沖突列表查找元素*/ while(he) { if (dictCompareKeys(d, key, he->key)) return he; he = he->next; } if (!dictIsRehashing(d)) return NULL; } return NULL; }
刪除元素首先查找元素, 然后將元素從hash表中移除即可, 調(diào)用dictDelete刪除元素, 會(huì)同時(shí)刪除元素所占空間
int dictDelete(dict *ht, const void *key) { return dictGenericDelete(ht,key,0); } static int dictGenericDelete(dict *d, const void *key, int nofree) { unsigned int h, idx; dictEntry *he, *prevHe; int table; if (d->ht[0].size == 0) return DICT_ERR; if (dictIsRehashing(d)) _dictRehashStep(d); h = dictHashKey(d, key); for (table = 0; table <= 1; table++) { idx = h & d->ht[table].sizemask; he = d->ht[table].table[idx]; prevHe = NULL; /*查找元素到元素,進(jìn)行刪除操作, 并釋放占用的內(nèi)存*/ while(he) { if (dictCompareKeys(d, key, he->key)) { /* Unlink the element from the list */ if (prevHe) prevHe->next = he->next; else d->ht[table].table[idx] = he->next; if (!nofree) { dictFreeKey(d, he); dictFreeVal(d, he); } zfree(he); d->ht[table].used--; return DICT_OK; } prevHe = he; he = he->next; } if (!dictIsRehashing(d)) break; } return DICT_ERR; /* not found */ }
hash命令操作都比較簡(jiǎn)單,需要注意的是當(dāng)我們創(chuàng)建hash表示默認(rèn)存儲(chǔ)結(jié)構(gòu),并不是dict,而是ziplist結(jié)構(gòu),可以參考redis之Ziplist數(shù)據(jù)結(jié)構(gòu),hash_max_ziplist_entries和hash_max_ziplist_value值作為閥值,hash_max_ziplist_entries表示一旦ziplist中元素?cái)?shù)量超過(guò)該值,則需要轉(zhuǎn)換為dict結(jié)構(gòu);hash_max_ziplist_value表示一旦ziplist中數(shù)據(jù)長(zhǎng)度大于該值,則需要轉(zhuǎn)換為dict結(jié)構(gòu)。
以上是redis中hash如何實(shí)現(xiàn)的的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。