Redis源码分析-基础功能(hash)

Redis(hash)

基础结构

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    int iterators; /* number of iterators currently running */

} dict;

其中type包含一系列哈希表需要用的函数,dictht类型的数组ht[2]表示两个哈希表实例,由rehashidx指明下一个需要扩容的哈希实例的编号,iterators记录外部使用哈希表的迭代器的数目。

其中type类型定义如下:

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;

其中包括对key值的复制,value的复制,key值比较,构造,析构,hash函数。

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;

} dictht;

dictht为哈希表具体实现的结构体,table指向哈希中的记录,用数组+开链的形式保存记录;size表示哈希表桶的大小,为2的指数;sizemark=size-1,方便哈希值根据size取模;used记录了哈希表中的记录数目。

自动扩容

Redis的hash表的容量不够时可以0/1切换,自动扩容。

void dictEnableResize(void) {
    dict_can_resize = 1;
}
void dictDisableResize(void) {
    dict_can_resize = 0;
}

通过以上2个函数表示当前hash表是否可以自动扩容。可以通过这两个函数设置当前hash表是否可以自动扩容。

/* Expand the hash table if needed */
static int _dictExpandIfNeeded(dict *d)
{
    /* Incremental rehashing already in progress. Return. */
    if (dictIsRehashing(d)) return DICT_OK;


    /* If the hash table is empty expand it to the initial size. */
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);


    /* If we reached the 1:1 ratio, and we are allowed to resize the hash
     * table (global setting) or we should avoid it but the ratio between
     * elements/buckets is over the "safe" threshold, we resize doubling
     * the number of buckets. */
    if (d->ht[0].used >= d->ht[0].size &&
        (dict_can_resize ||
         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
    {
        return dictExpand(d, d->ht[0].used*2);
    }
    return DICT_OK;

}

应用程序可以使用dictResize()扩容,它首先判断是否允许扩容,及是否正在扩容。若可以扩容,则调用dictExpand()扩容。然后应用程序需要调用dictRehashMilliseconds()启动扩容过程,并指定扩容过程中记录拷贝速度。除了应用程序指定的扩容外,在调用dictAdd()往哈希中添加记录时,系统也会通过调用_dictExpandIfNeeded()判断是否需要扩容。_dictExpandIfNeeded()中,如果正在扩容,则不会重复进行扩容;如果哈希表size=0,即桶数目为0,则扩容到初始大小;否则如果used>=size,并且can_resize==1或used/size超过阀值(默认为5)时,以max(used, size)的两倍为基数,调用dictExpand()扩容。


迭代器

dictGetIterator()获取迭代器:

dictIterator *dictGetIterator(dict *d)
{
    dictIterator *iter = zmalloc(sizeof(*iter));
    iter->d = d;
    iter->table = 0;
    iter->index = -1;
    iter->safe = 0;
    iter->entry = NULL;
    iter->nextEntry = NULL;
    return iter;
}

迭代器迭代器提供了遍历哈希表中所有元素的方法。

通过dictGetIterator()获得迭代器后,使用dictNext(dictIterator *)方法获得下一个元素。

dictEntry *dictNext(dictIterator *iter)
{
    while (1) {
        if (iter->entry == NULL) {
            dictht *ht = &iter->d->ht[iter->table];
            if (iter->index == -1 && iter->table == 0) {
                if (iter->safe)
                    iter->d->iterators++;
                else
                    iter->fingerprint = dictFingerprint(iter->d);
            }
            iter->index++;
            if (iter->index >= (long) ht->size) {
                if (dictIsRehashing(iter->d) && iter->table == 0) {
                    iter->table++;
                    iter->index = 0;
                    ht = &iter->d->ht[1];
                } else {
                    break;
                }
            }
            iter->entry = ht->table[iter->index];
        } else {
            iter->entry = iter->nextEntry;
        }
        if (iter->entry) {
            /* We need to save the 'next' here, the iterator user
             * may delete the entry we are returning. */
            iter->nextEntry = iter->entry->next;
            return iter->entry;
        }
    }
    return NULL;

}

当外部持有的迭代器数目不为0时,哈希表会暂停扩容操作。这时开始遍历,保证遍历的数目是最新的。迭代器遍历的过程,从ht[0]开始,依次从第一个桶table[0]开始遍历桶中的元素,然后时table[1], table[2], ..., table[size],若正在扩容,则会继续遍历ht[1]中的桶。遍历桶中元素时,依次访问链表中的每个元素。

参考《Redis源代码分析》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值