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

ddrv8njm  于 9个月前  发布在  Redis
关注(0)|答案(1)|浏览(87)

Redis有一个SCAN命令,可以用来搜索匹配模式的键等。
Redis SCAN doc
你首先给一个游标值0;每个调用返回一个新的游标值,你把它传递给下一个SCAN调用。值0表示迭代完成。假设不需要服务器或客户端状态(除了游标值)
我想知道Redis是如何实现扫描算法的?

pvabu6sv

pvabu6sv1#

你可以在redis的dict.c源文件中找到答案。然后我将引用它的一部分。
迭代的工作方式如下:
1.最初,您使用cursor(v)值0调用该函数.
1.该函数执行迭代的一个步骤,并返回
必须在下一次调用中使用的新游标值。
1.当返回的游标为0时,迭代完成。“
此函数保证在迭代开始和结束之间返回字典中的所有元素。但是,某些元素可能会多次返回。对于每个返回的元素,都会调用回调参数'fn',并将'privdata'作为第一个参数,将字典条目'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.我们可能会多次返回元素,但这通常在应用程序级别很容易处理。
1.迭代器每次调用必须返回多个元素,因为它需要总是返回给定桶中链接的所有键和所有扩展,所以我们确信在重新散列过程中不会错过键的移动。
1.反向光标一开始有点难以理解,但这个注解应该会有所帮助。

相关问题