[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(2的n次方幂);
也没什么算法,就是从初始大小开始一直*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