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

sea*_*and 9 redis

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

Redis SCAN doc

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

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

mis*_*ion 19

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

迭代的工作方式如下:

  1. 最初使用游标(v)值0调用该函数.2)
  2. 该函数执行迭代的一个步骤,并返回
    您在下一次调用中必须使用的新游标值.
  3. 当返回的游标为0时,迭代完成.

该函数保证字典中存在的所有元素在迭代的开始和结束之间返回.但是有些元素可能会多次返回.对于返回的每个元素,回调参数'fn'被调用,'privdata'作为第一个参数,字典entry'de'作为第二个参数.

这个怎么运作

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

需要此策略,因为可以在迭代调用之间调整哈希表的大小.dict.c哈希表总是大小为2的幂,它们使用链接,因此给定表中元素的位置是通过计算Hash(key)和SIZE-1之间的按位AND给出的(其中SIZE-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. 反向光标起初有点难以理解,但这个评论应该有所帮助.

  • 我花了相当多的时间来写这篇评论,看到它发布在这里是一种解脱:-)谢谢. (5认同)
  • 我真的很喜欢Redis.看到干净,可读的源代码让我更喜欢它:) (2认同)