Redis,SCAN游标“状态管理”如何工作?

时间:2015-01-23 02:38:13

标签: redis

Redis有一个SCAN命令,可用于迭代匹配模式等的键。

Redis SCAN doc

首先给出游标值为0;每次调用都会返回一个新的游标值,您将其传递给下一个SCAN调用。值为0表示迭代结束。据说不需要服务器或客户端状态(光标值除外)

我想知道Redis如何实施扫描算法?

1 个答案:

答案 0 :(得分:17)

您可以在redis dict.c源文件中找到答案。然后我会引用它的一部分。

迭代按以下方式工作:

  1. 最初使用游标(v)值0来调用该函数.2)
  2. 该函数执行迭代的一个步骤,并返回
    您必须在下次通话中使用的新光标值。
  3. 当返回的光标为0时,迭代完成。
  4. 该函数保证字典中存在的所有元素在迭代的开始和结束之间返回。但是有些元素可能会多次返回。对于返回的每个元素,回调参数' fn'被称为'privdata'作为第一个参数和字典条目' de'作为第二个论点。

    如何运作

    迭代算法由Pieter Noordhuis设计。主要思想是从高阶位开始增加光标。也就是说,不是正常递增光标,而是反转光标的位,然后光标递增,最后再次反转。

    需要此策略,因为可以在迭代调用之间调整哈希表的大小。 dict.c哈希表的大小总是2的幂,并且它们使用链接,因此通过计算哈希(键)和SIZE-1(其中SIZE-)之间的按位AND来给出给定表中元素的位置。 1总是掩码,相当于在键的Hash和SIZE之间进行剩余的除法。)

    例如,如果当前哈希表大小为16,则掩码为(二进制)1111.哈希表中键的位置将始终是哈希输出的最后四位,依此类推。

    如果表格的大小发生变化会怎样?

    如果哈希表增长,元素可以在旧桶的一个倍数中的任何位置:例如,让我们说我们已经用4位光标1100迭代(掩码是1111,因为哈希表大小= 16)

    如果哈希表将调整为64个元素,那么新的掩码将是111111.通过用?或1100替换0或1获得的新桶只能通过我们在扫描时访问过的键来定位。存储桶1100在较小的哈希表中。

    首先迭代高位,因为反转计数器,如果表大小变大,则无需重新启动光标。它将继续使用没有' 1100'的游标进行迭代。最后,也没有已经探索过的最后4位的任何其他组合。

    类似地,当表格大小随时间缩小时,例如从16变为8,如果已经完全探索了较低三位(大小为8的掩码为111)的组合,则不会再次访问它,因为我们我们确定我们尝试过,例如,0111和1111(高位的所有变体),所以我们不需要再次测试它。

    等等......在重组过程中你有两个表!

    是的,这是真的,但我们总是首先迭代较小的表,然后我们测试当前光标到较大表的所有扩展。例如,如果当前光标是101并且我们还有一个大小为16的较大表,我们还在较大的表内测试(0)101和(1)101。这将问题简化为只有一个表,如果存在,则较大的表只是较小表的扩展。

    限制

    这个迭代器是完全无状态的,这是一个巨大的优势,包括不使用额外的内存。 这种设计的缺点是:

    1. 我们可能不止一次返回元素。但是,这通常很容易在应用程序级别处理。
    2. 迭代器必须每次调用返回多个元素,因为它需要始终返回给定存储桶中链接的所有键以及所有扩展,因此我们确信在重新散列期间我们不会错过键移动。
    3. 反向光标起初有点难以理解,但这条评论应该有所帮助。