[toc]

redis rehash扩容的值到底是多少

这篇文章已经总结了 https://www.jianshu.com/p/a2fb3b727879

源码分析

首先看下这个方法:

/* 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;
}

_dictExpandIfNeeded判断hashtable是否需要扩容,可以看到判断条件只有一个: 当ht[0]的已使用>=ht[0]的size,并且可以重置size时,或者大于负载因子(写死的5)时,会执行扩容操作

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);
    }

上面的文章总结有一句话

哈希表的扩展与收缩
当以下条件中的任意一个被满足时, 程序会自动开始对哈希表执行扩展操作: 服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1 ;
服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5 ;

这段话实际是看dict_can_resize是否为1,dict_can_resize设置如下

void dictEnableResize(void) {
    dict_can_resize = 1;
}

void dictDisableResize(void) {
    dict_can_resize = 0;
}

//server.c
void updateDictResizePolicy(void) {
    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
        dictEnableResize();
    else
        dictDisableResize();
}

redis会判断是否有正在进行rdb或者aof的子进程,如果没有在进行rdb或者aof,会将dict_can_resize设置为1,此时只要used>=size就会进行扩容.

再看一下具体的扩容函数

/* Expand or create the hash table */
int dictExpand(dict *d, unsigned long size)
{
    /* the size is invalid if it is smaller than the number of
     * elements already inside the hash table */
    if (dictIsRehashing(d) || d->ht[0].used > size)
        return DICT_ERR;

    dictht n; /* the new hash table */
    unsigned long realsize = _dictNextPower(size);

    /* Rehashing to the same table size is not useful. */
    if (realsize == d->ht[0].size) return DICT_ERR;

    /* Allocate the new hash table and initialize all pointers to NULL */
    n.size = realsize;
    n.sizemask = realsize-1;
    n.table = zcalloc(realsize*sizeof(dictEntry*));
    n.used = 0;

    /* Is this the first initialization? If so it's not really a rehashing
     * we just set the first hash table so that it can accept keys. */
    if (d->ht[0].table == NULL) {
        d->ht[0] = n;
        return DICT_OK;
    }

    /* Prepare a second hash table for incremental rehashing */
    d->ht[1] = n;
    d->rehashidx = 0;
    return DICT_OK;
}

/* Our hash table capability is a power of two */
static unsigned long _dictNextPower(unsigned long size)
{
    //初始值为4
    unsigned long i = DICT_HT_INITIAL_SIZE;

    if (size >= LONG_MAX) return LONG_MAX + 1LU;

    while(1) {
        if (i >= size)
            return i;
        i *= 2;
    }
}

上面可以看出ht[1]的size的值是在_dictNextPower函数中确定的,即

如果执行的是扩展操作, 那么ht[1]的大小为第一个大于等于ht[0].used * 2的 2^n(2n次方幂);

也没什么算法,就是从初始大小开始一直*2,直到找到大于等于size的值为止。


在_dictExpandIfNeeded函数中 传入dictExpand中的size为d->ht[0].used*2,所以可以总结为:

  • 当达到临界值(used=size)时,没有进行rdb或者aof操作,此时扩容的大小为ht[0]的两倍
  • 进行rdb或者aof操作并且达到负载因子时,此时扩容为第一个大于等于ht[0].used * 2的 2^n
  • 当达到临界值(used>=size)时,正在进行rdb或者aof,但是还没达到负载因子时,rdb或aof结束,此时used>size,此时扩容为第一个大于等于ht[0].used * 2的 2^n

results matching ""

    No results matching ""