【备份】redis源码剖析ITeye - 众发娱乐

【备份】redis源码剖析ITeye

2019年02月20日09时52分12秒 | 作者: 山菡 | 标签: 调用,时分,个数 | 浏览: 399

原文地址:

dict完结中首要用到如下结构体,其实就是个典型的链式hash。

一个dict会有2个hash table,由dictht结构办理,编号为0和1.

运用是优先运用0号hash table,当空间缺乏时会调用dictExpand来扩展hash table,此刻预备1号hash table用于增量的rehash运用。rehash完结后把0号开释,1号保存到0号。

rehashidx是下一个需求rehash的项在ht[0]中的索引,不需求rehash时置为-1。也就是说-1时,表明不进行rehash。

iterators记载当时dict中的迭代器数,首要是为了防止在有迭代器时rehash,在有迭代器时rehash或许会形成值的丢掉或重复,

dictht中的table是一个数组+指针方式的hash表,size表hash数组(桶)的巨细,used表明hash表的元素个数,这两个值与rehash、resize进程密切相关。sizemask等于size-1,这是为了便利将hash值映射到数组中。

typedef struct dictEntry {
void *key;
void *val;
struct dictEntry *next;
} dictEntry;
typedef struct dictht {
dictEntry **table;
unsigned long size;//hash桶的个数
unsigned long sizemask;//hash取模的用到
unsigned long used;//元素个数
} dictht;
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
int rehashidx; /* rehashing not in progress if rehashidx  -1 */
int iterators; /* number of iterators currently running */
} dict;
typedef struct dictIterator {
dict *d;
int table;
int index;
dictEntry *entry, *nextEntry;
} dictIterator;

rehash有2种作业形式

lazy rehashing:在每次对dict进行操作的时分履行一个slot的rehash

active rehashing:每100ms里边运用1ms时刻进行rehash。

什么时分dict做扩容

在数据刺进的时分会调用dictKeyIndex,该办法里会调用_dictExpandIfNeeded,判别dict是否需求rehash,当dict中桶的个数大于元素的个数时,调用dictExpand扩展hash

/* Expand the hash table if needed */ static int _dictExpandIfNeeded(dict *d) /* If the hash table is empty expand it to the intial size, * if the table is “full” dobule its size. */ if (dictIsRehashing(d)) return DICT_OK; if (d- ht[0].size 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); if (d- ht[0].used = d- ht[0].size dict_can_resize) return dictExpand(d, ((d- ht[0].size d- ht[0].used) ? d- ht[0].size : d- ht[0].used)*2); return DICT_OK;

dictExpand的作业首要是初始化hash表,默许是扩展两倍(并不单纯是桶的两倍),然后赋值给ht[1],然后状况改为rehashing,此刻该dict开端rehashing

扩容进程怎么进行

rehash首要在dictRehash中完结。先看下什么时分进行rehash。

active rehashing :serverCron中,当没有后台子线程时,会调用incrementallyRehash,终究调用dictRehashMilliseconds。incrementallyRehash的时刻较长,rehash的个数也比较多。这儿每次履行 1 millisecond rehash 操作;假如未完结 rehash,会在下一个 loop 里边持续履行。

/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */

int dictRehashMilliseconds(dict *d, int ms) {

long long start = timeInMilliseconds();

int rehashes = 0;

while(dictRehash(d,100)) {

rehashes += 100;

if (timeInMilliseconds()-start   ms) break;

}

return rehashes;

}

lazy rehashing:_dictRehashStep中,也会调用dictRehash,而_dictRehashStep每次仅会rehash一个值从ht[0]到 ht[1],但因为_dictRehashStep是被dictGetRandomKey、dictFind、 dictGenericDelete、dictAdd调用的,因而在每次dict增删查改时都会被调用,这无疑就加速rehash了进程。

我 们再来看看做rehash的办法。dictRehash每次增量rehash n个元素,因为在主动调整巨细时已设置好了ht[1]的巨细,因而rehash的首要进程就是遍历ht[0],获得key,然后将该key按ht[1]的 桶的巨细从头rehash,并在rehash完后将ht[0]指向ht[1],然后将ht[1]清空。在这个进程中rehashidx非常重要,它表明前次rehash时在ht[0]的下标方位。

int dictRehash(dict *d, int n) {

if (!dictIsRehashing(d)) return 0;

while(n–) {

dictEntry *de, *nextde;

/* Check if we already rehashed the whole table… */

if (d- ht[0].used  0) {

_dictFree(d- ht[0].table);

d- ht[0] = d- ht[1];//1幅值给0

_dictReset( d- ht[1]);//rehash完结后把0号开释,1号保存到0号

d- rehashidx = -1;

return 0;

}

/* Note that rehashidx can’t overflow as we are sure there are more

* elements because ht[0].used != 0 */

while(d- ht[0].table[d- rehashidx]  NULL) d- rehashidx++;//在当时桶为null,找下一个

de = d- ht[0].table[d- rehashidx];

/* Move all the keys in this bucket from the old to the new hash HT */

while(de) {

unsigned int h;

nextde = de- next;

/* Get the index in the new hash table */

h = dictHashKey(d, de- key)   d- ht[1].sizemask;

de- next = d- ht[1].table[h];

d- ht[1].table[h] = de;

d- ht[0].used–;

d- ht[1].used++;

de = nextde;

}

d- ht[0].table[d- rehashidx] = NULL;//rehash过的ht[0]桶置为null

d- rehashidx++;//偏移+1

}

return 1;

}

能够看到,redis对dict的rehash是分批进行的,这样不会堵塞恳求,规划的比较高雅。

但是在调用dictFind的时分,或许需求对两张dict表做查询。仅有的优化判别是,当key在ht[0]不存在且不在rehashing状况时,能够速度返回空。假如在rehashing状况,当在ht[0]没值的时分,还需求在ht[1]里查找。

dictAdd的时分,假如状况是rehashing,则把值刺进到ht[1],不然ht[0]

 

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表众发娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章

阅读排行

  • 1

    【备份】redis源码剖析ITeye

    调用,时分,个数
  • 2
  • 3

    sql2000 1433端口未翻开ITeye

    补丁,客户端,端口
  • 4

    JDBC衔接数据库众发娱乐

    数据库,句子,成果
  • 5
  • 6

    Oracle 简略运用huabian

    运用,数据库,用户
  • 7
  • 8
  • 9

    MySql标准fenghuang

    标准,进程,修正
  • 10

    数据库相关itjob

    触发器,时刻