当前位置:网站首页>redis 源码分析3 间断遍历的实现
redis 源码分析3 间断遍历的实现
2022-07-17 05:08:00 【HjasnJH】
全量遍历问题,会导致整个redis不可用,所以就引入了间断遍历
dictscan实现间断遍历
dictscan的实现
unsigned long dictScan(dict *d,
unsigned long v,
dictScanFunction *fn,
dictScanBucketFunction* bucketfn,
void *privdata)
{
dictht *t0, *t1;
const dictEntry *de, *next;
unsigned long m0, m1;
if (dictSize(d) == 0) return 0;
if (!dictIsRehashing(d)) {
t0 = &(d->ht[0]);
m0 = t0->sizemask;
/* Emit entries at cursor */
if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);
de = t0->table[v & m0];
while (de) {
next = de->next;
fn(privdata, de);
de = next;
}
/* Set unmasked bits so incrementing the reversed cursor
* operates on the masked bits */
v |= ~m0;
/* Increment the reverse cursor */
v = rev(v);
v++;
v = rev(v);
} else {
t0 = &d->ht[0];
t1 = &d->ht[1];
/* Make sure t0 is the smaller and t1 is the bigger table */
if (t0->size > t1->size) {
t0 = &d->ht[1];
t1 = &d->ht[0];
}
m0 = t0->sizemask;
m1 = t1->sizemask;
/* Emit entries at cursor */
if (bucketfn) bucketfn(privdata, &t0->table[v & m0]);
de = t0->table[v & m0];
while (de) {
next = de->next;
fn(privdata, de);
de = next;
}
/* Iterate over indices in larger table that are the expansion
* of the index pointed to by the cursor in the smaller table */
do {
/* Emit entries at cursor */
if (bucketfn) bucketfn(privdata, &t1->table[v & m1]);
de = t1->table[v & m1];
while (de) {
next = de->next;
fn(privdata, de);
de = next;
}
/* Increment the reverse cursor not covered by the smaller mask.*/
v |= ~m1;
v = rev(v);
v++;
v = rev(v);
/* Continue while bits covered by mask difference is non-zero */
} while (v & (m0 ^ m1));
}
return v;
}
间断遍历有三种情况:
1) 从迭代开始到结束, 散列表没有进行rehash操作。
2) 从迭代开始到结束, 散列表进行了扩容或缩容操作, 且恰好为两次迭代间隔期间完成了rehash操作。
3) 从迭代开始到结束, 某次或某几次迭代时散列表正在进行rehash操作
间断遍历的原理
其中最重要的一点要掌握的,就是字典的扩容和缩容后,原来的数据在新的字典中的位置会在哪里?
比如扩容前,假设长度从4 扩展到8,原来在0位置的元素的,在新的哈希表中,只能在0或者4的位置,算法就是利用了这种特性,来实现不重复遍历和不漏遍历。缩容的原理也一样。

其中,我就列举其中的一种情况,来看算法是怎么实现规避这种重复的遍历的现象
假设现在的容量位4,在第三次迭代时,哈希表发生了扩容到8,
/* Increment the reverse cursor not covered by the smaller mask.*/
v |= ~m1;
v = rev(v);
v++;
v = rev(v);数组的掩码为m0=0x11, ~m0=0x100
则前两次遍历如下:

这时候重新rehash
数组的掩码m0=0x111,~m0=0x1000

此时就已经结束遍历结束了,我们看看总共遍历了哪几个
旧的hash表:0 2
新的hash表:1 5 3 7
这时候是不是觉得是不是漏了?
其实,我们从新的hash表的角度看,其实遍历旧hash表的0 2,其实时遍历了新的hash表的 0 4 2 6,因为0-> 0 4 , 2-> 2 6,所以实际上就实现了0 4 2 6 1 5 3 7的全遍历。
其他情况类似。这就是这个reverse binary iteration(反向二进制迭代算法)。
边栏推荐
猜你喜欢

【C语言—零基础第六课】输入输出语句格式与复合语句

Internship project 2 - Homepage configuration - my data module

Cesium 綁定鼠標事件和移除鼠標事件

【AI】利用简单神经网络做动作识别——基于coco关键点

ArcMap 创建常量栅格并镶嵌至新栅格

Applet editor rich text editing and rich text parsing

The first smart contract program faucet sol

Excel calculates the remaining days of the month

实习项目1-个性化主页配置

手把手教你复现Log4j2核弹级漏洞
随机推荐
[ES6] use multiple functions such as adding data, filtering and transmitting to the page to realize cases
uni-app 条件编译#ifdef #endif 兼容多个终端
IText modify PDF Text
Swagger配置与使用
Two methods of rotation chart and automatic rotation
Two JS methods of rolling wheel loading and modal box dragging
Internship project 3- change owner
Installation and fast use of Mongo DB stand-alone version
The principle and local storage of the throttle valve of the rotation chart are summarized
Use (offset, page) in JS to achieve login effect
Ucharts chart, pie chart, bar chart and line chart are used in uniapp
Wechat applet cloud development and use method-1
2020-11-10
es6新增-字符串部分
vlookup函数的使用方法及实例
ES6 real case deconstruction (multidimensional array object) new case:
MySQL optimization
获取数组中对象内部的数值最大与最小值多功能版及点名系统完整版并展示效果
Case summary of rotation chart moving speed (constant speed, slow motion)
Pointer advanced simple summary