当redis数据库中HashTable数据达到触发条件时,会触发哈希表的重构的操作。
触发操作同时需要检测server是否有持久化操作,即检测持久化进程是否存在,如果存在那么rehash过程不会操作。因为当有单独进程在进行持久化操作时,会引起数据差异化,即持久化进程所持有的的hash表数据,和主进程所持有的hash表数据会不同。只有在进程创建的那一刻两者的数据时一致的,这是在创建进程时的copy-on-write 引起的。
redis为了兼顾性能的考虑,分为lazy和active的两种rehash操作,同时进行,直到rehash完成。下面会看一下这两种rehash操作:
1、Resize缩小哈希表触发条件是:
元素数量/槽的数量小于REDIS_HT_MINFILL时,触发dictResize操作
int htNeedsResize(dict *dict) {
long long size, used;
size = dictSlots(dict);
used = dictSize(dict);
return (size && used && size > DICT_HT_INITIAL_SIZE &&
(used*100/size < REDIS_HT_MINFILL));
}
/* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL
* we resize the hash table to save memory */
void tryResizeHashTables(int dbid) {
if (htNeedsResize(server.db[dbid].dict))
dictResize(server.db[dbid].dict);
if (htNeedsResize(server.db[dbid].expires))
dictResize(server.db[dbid].expires);
}
2、触发哈希表扩大的条件:
哈希表中元素的数量大于槽的数量或者元素的数量/槽的数量大于dict_force_resize_ratio时触发 扩大操作。
成倍扩大哈希表
/* 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 nu