Redis源码解析:04字典的遍历dictScan

         dict.c中的dictScan函数,用来遍历字典,迭代其中的每个元素。该函数使用的算法非常精妙!!!所以必须记录一下。

         遍历一个稳定的字典,当然不是什么难事,但Redis中的字典因为有rehash的过程,使字典可能扩展,也可能缩小。这就带来了问题,如果在两次遍历中间,字典的结构发生了变化(扩展或缩小),字典中的元素所在的位置相应的会发生变化,那如何保证字典中原有的元素都可以被遍历?又如何能尽可能少的重复迭代呢?

         这就是该算法的精妙所在,使用该算法,可以做到下面两点:

         a:开始遍历那一刻的所有元素,只要不被删除,肯定能被遍历到,不管字典扩展还是缩小;

         b:该算法可能会返回重复元素,但是已经把返回重复元素的可能性降到了最低;

 

一:游标cursor的演变

         该算法使用了游标cursor来遍历字典,它表示本次要访问的bucket的索引。bucket中保存了一个链表,因此每次迭代都会把该bucket的链表中的所有元素都遍历一遍。

         第一次迭代时,cursor置为0,dictScan函数的返回值作为下一个cursor再次调用dictScan,最终,dictScan函数返回0表示迭代结束。

         首先看一下cursor的演变过程,也是该算法的核心所在。这里cursor的演变是采用了reverse binary iteration方法,也就是每次是向cursor的最高位加1,并向低位方向进位。

         下面具体解释,首先,根据dictScan写一个简单的测试函数,用来看cursor的演变过程:

void test_dictScan_cursor(int tablesize)
{
    unsigned long v;
    unsigned long m0;

    v = 0;
    m0 = tablesize-1;

    printbits(v, (int)log2(tablesize));
    printf(" --> ");

    do
    {   
        v |= ~m0;
        v = rev(v);
        v++;      
        v = rev(v);

        printbits(v, (int)log2(tablesize));
        printf(" --> ");
    }while (v != 0);    
    
    printf("\b\b\b\b\b     \n");
}

         参数tablesize表示哈希表的大小,printbits用来打印v的低n二进制位,n等于log2(tablesize),以tablesize为8和16分别运行该函数,结果如下:

000 --> 100 --> 010 --> 110 --> 001 --> 101 --> 011 --> 111 --> 000   

0000 --> 1000 --> 0100 --> 1100 --> 0010 --> 1010 --> 0110 --> 1110 --> 0001 --> 1001 --> 0101 --> 1101 --> 0011 --> 1011 --> 0111 --> 1111 --> 0000   

         这就是所谓的reverse binaryiteration方法,也就是每次是向v的最高位加1,并向低位方向进位。比如1101的下一个数是0011,因为1101的前三个数为110,最高位加1,并且向低位进位就是001,所以最终得到0011。

 

         在Redis中,字典的哈希表长度始终为2的n次方。因此m0始终是一个低n位全为1,其余为全为0的数。整个计算过程,都是在v的低n位数中进行的,比如长度为16的哈希表,则n=4,因此v是从0到15这几个数之间的转换。下面解释一下计算过程:

         第一步:v |= ~m0;                 //用于保留v的低n位数,其余位全置为1:

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值