当前位置:网站首页>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(反向二进制迭代算法)。
边栏推荐
- 微信小程序获取年月日周及早上、中午、晚上
- [ES6] quickly print user information to the page
- 循环赛制日程表问题
- 百度地图 实现 热力图
- uni-app 条件编译#ifdef #endif 兼容多个终端
- 使用js中的(offset,page)实现登录效果
- STL container -- basic operation of map
- CityEngine 三维管道建模教程
- 2020-11-10
- Solve the problem of inconsistent prediction effect between text detection training model and information model based on paddleocr
猜你喜欢
随机推荐
Internship project 2 - Homepage configuration - my data module
【Es6】forEach,for... in ,for... Of column, which allows you to quickly distinguish the usage and differences of various for statements through project cases (full version). There are detailed notes ins
【AI】利用简单神经网络做动作识别——基于coco关键点
STL容器——set集合的应用
父组件加scoped有时也会影响子组件
es6新增-数组/对象的解构赋值
Pat class B 1002: write this number
es6新增-数组部分
Email (including attachments, Netease, QQ)
web3js开发技术
[ES6] quickly print user information to the page
【LeetCode——编程能力入门第二天】运算符(位1的个数/整数的各位积和之差)
The principle and local storage of the throttle valve of the rotation chart are summarized
Two methods of obtaining URL parameters and various methods of obtaining location objects
Shell script configures root to login to other hosts without secret
实习项目1-个性化主页配置
【LeetCode——编程能力入门第一天】基本数据类型[在区间范围内统计奇数数目/去掉最低工资和最高工资后的工资平均值)
(elaborate) ES6 remaining parameters, ES6 built-in objects, template string content (detailed example Dictionary) and practical cases of flexible use of the project
Internship project 3- change owner
第一个智能合约程序Faucet.sol