redis中hash如何實(shí)現(xiàn)的

這篇文章主要介紹redis中hash如何實(shí)現(xiàn)的,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

專注于為中小企業(yè)提供網(wǎng)站設(shè)計(jì)、做網(wǎng)站服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)寶塔免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了數(shù)千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

0.前言

redis是KV型的內(nèi)存數(shù)據(jù)庫, 數(shù)據(jù)庫存儲的核心就是Hash表, 我們執(zhí)行select命令選擇一個(gè)存儲的db之后, 所有的操作都是以hash表為基礎(chǔ)的, 下面會分析下redis的hash數(shù)據(jù)結(jié)構(gòu)和實(shí)現(xiàn).

1.hash數(shù)據(jù)結(jié)構(gòu)

/*Hash表一個(gè)節(jié)點(diǎn)包含Key,Value數(shù)據(jù)對 */
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;

/* 存儲不同數(shù)據(jù)類型對應(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表示沒有進(jìn)行rehash */
    int iterators; /*  */
} dict;

2.hash數(shù)據(jù)結(jié)構(gòu)圖

redis中hash如何實(shí)現(xiàn)的

3.漸進(jìn)式hash說明

dict中ht[2]中有兩個(gè)hash表, 我們第一次存儲數(shù)據(jù)的數(shù)據(jù)時(shí), ht[0]會創(chuàng)建一個(gè)最小為4的hash表, 一旦ht[0]中的size和used相等, 則dict中會在ht[1]創(chuàng)建一個(gè)size*2大小的hash表, 此時(shí)并不會直接將ht[0]中的數(shù)據(jù)copy進(jìn)ht[0]中, 執(zhí)行的是漸進(jìn)式rehash, 即在以后的操作(find, set, get等)中慢慢的copy進(jìn)去, 以后新添加的元素會添加進(jìn)ht[0], 因此在ht[1]被占滿的時(shí)候定能確保ht[0]中所有的數(shù)據(jù)全部copy到ht[1]中.

4.創(chuàng)建hash表

創(chuàng)建hash表過程非常簡單,直接調(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;
}

5.添加元素

hash表中添加元素,首先判斷空間是否足夠, 然后計(jì)算key對應(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過程,則新元素添加到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對應(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í)分配了原來兩倍的內(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);

    /* 已使用空間滿足不了設(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;
     /* 對需要分配大小圓整為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;
}

6.查找元素

查找元素過程,首先計(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;
}

7.刪除元素

刪除元素首先查找元素, 然后將元素從hash表中移除即可, 調(diào)用dictDelete刪除元素, 會同時(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命令

hash命令操作都比較簡單,需要注意的是當(dāng)我們創(chuàng)建hash表示默認(rèn)存儲結(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ù)量超過該值,則需要轉(zhuǎn)換為dict結(jié)構(gòu);hash_max_ziplist_value表示一旦ziplist中數(shù)據(jù)長度大于該值,則需要轉(zhuǎn)換為dict結(jié)構(gòu)。

以上是redis中hash如何實(shí)現(xiàn)的的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)頁標(biāo)題:redis中hash如何實(shí)現(xiàn)的
當(dāng)前網(wǎng)址:http://muchs.cn/article8/jcpdop.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站改版、ChatGPT、網(wǎng)站設(shè)計(jì)公司、用戶體驗(yàn)、網(wǎng)站排名、網(wǎng)站營銷

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站托管運(yùn)營