当前位置:网站首页>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(反向二进制迭代算法)。
边栏推荐
猜你喜欢

Wechat applet obtains the week, morning, noon and evening of month, year and day
![[AI] action recognition using simple neural network -- Based on coco key points](/img/67/cd6be6e070fb5d4d44ee043ebd7fac.png)
[AI] action recognition using simple neural network -- Based on coco key points

微信小程序 学习笔记

Shell脚本配置root免密登录到其他主机

web3js开发技术

【LeetCode——编程能力入门第一天】基本数据类型[在区间范围内统计奇数数目/去掉最低工资和最高工资后的工资平均值)

uni-app 条件编译#ifdef #endif 兼容多个终端

百度地图 实现 热力图

The first smart contract program faucet sol

es6新增-Symbol数据类型
随机推荐
Easypoi之excel简单导出
Swagger配置与使用
Wechat applet wx Setclipboarddata copy text
字幕文件与视频文件对不上的处理方式
路由器loopback口实验
Excel calculates the remaining days of the month
Applet cloud development upload pictures to cloud storage
Cesium 綁定鼠標事件和移除鼠標事件
cookie是否有效时间限定?如何设置cookie?手把手教你设置
网络命令:网卡信息,netstat,arp
Get the multi-functional version of the maximum and minimum values of the internal values of the objects in the array and the full version of the roll call system, and show the effect
微信小程序 学习笔记
uniapp 表单(input、radio、picker)提交获取参数值
STL容器——queue与deque的基本操作
轮播图的两种方法及自动轮播
【C语言—零基础第七课】顺序结构与选择结构
PAT乙级1002:写出这个数
Log4j的使用
Two JS methods of rolling wheel loading and modal box dragging
Web3js development technology