scan
SCAN
SCAN cursor [MATCH pattern] [COUNT count]
自2.8.0起可用。
时间复杂度:
O(1)用于每个呼叫。O(N)进行完整的迭代,包括足够的命令调用以使游标返回0. N是集合中元素的数量。
使用 SCAN 命令和密切相关的命令 SSCAN,HSCAN 和 ZSCAN 来递增地遍历一组元素。
- SCAN 迭代当前选定的 Redis 数据库中的一组键。
- SSCAN 迭代 Sets 类型的元素。
- HSCAN 迭代哈希类型的字段及其关联值。
- ZSCAN 迭代 Sorted Set 类型的元素及其相关分数。
由于这些命令允许增量迭代,每次调用只返回少量元素,因此可以在生产中使用它们,而无需像 KEYS 或 SMEMBERS 这样的命令可能会在被调用时长时间(甚至几秒)阻塞服务器的缺点键或元素的大集合。
然而,尽管像 SMEMBERS 这样的阻塞命令能够在给定时刻提供集合中的所有元素,但 SCAN 系列命令仅对返回的元素提供有限的保证,因为我们递增迭代的集合可能在迭代过程中发生更改。
请注意,SCAN,SSCAN,HSCAN 和 ZSCAN 的工作方式非常相似,因此本文档涵盖了所有四个命令。然而,一个明显的区别是,对于SSCAN,HSCAN 和 ZSCAN,第一个参数是保存 Set,Hash 或 Sorted Set 值的密钥的名称。SCAN 命令不需要任何键名参数,因为它在当前数据库中迭代键,所以迭代对象就是数据库本身。
SCAN的基本用法
SCAN 是一个基于游标的迭代器。这意味着在每次调用该命令时,服务器都会返回一个更新的游标,用户需要在下一次调用中将其用作游标参数。
当光标设置为0时开始迭代,当服务器返回的光标为0时终止迭代。以下是 SCAN 迭代的示例:
redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"
2) "key:8"
3) "key:4"
4) "key:14"
5) "key:16"
6) "key:17"
7) "key:15"
8) "key:10"
9) "key:3"
10) "key:7"
11) "key:1"
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
2) "key:18"
3) "key:0"
4) "key:2"
5) "key:19"
6) "key:13"
7) "key:6"
8) "key:9"
9) "key:11"
在上面的例子中,第一个调用使用零作为游标,以开始迭代。第二次调用使用前一次调用返回的游标作为回复的第一个元素,即17。
正如你所看到的,SCAN返回值
是一个包含两个值的数组:第一个值是下一次调用中使用的新游标,第二个值是元素数组。
由于在第二次调用中返回的游标是0,所以服务器向调用者发信号通知完成迭代,并且集合被完全探索。使用游标值0开始迭代,并调用 SCAN,直到返回的游标再次为0,称为完整迭代
。
扫描保证
SCAN 命令和 SCAN 系列中的其他命令能够向用户提供与完整迭代关联的一组保证。
- 完整迭代总是从完整迭代的开始到结束检索集合中存在的所有元素。这意味着如果迭代开始时给定元素位于集合内部,并且在迭代终止时仍然存在,那么在某个点 SCAN 将其返回给用户。
- 完整迭代从不会返回集合中不存在的任何元素,从完整迭代的开始到结束。因此,如果一个元素在迭代开始之前被移除,并且在迭代过程中永远不会将其添加回集合中,SCAN可以确保这个元素永远不会被返回。
但是由于 SCAN 只有很少的状态相关(只是光标),它有以下缺点:
- 给定的元素可能会多次返回。应用程序可以处理重复元素的情况,例如仅使用返回的元素来执行多次重新应用时安全的操作。
- 在整个迭代过程中并不总是出现在集合中的元素可能被返回或不被返回:它是未定义的。
每次 SCAN 呼叫返回的元素数量
SCAN 家族功能不保证每次通话返回的元素数量都在给定的范围内。这些命令还允许返回零个元素,并且只要返回的游标不为零,客户端就不应该考虑迭代完成。
然而,返回的元素的数量是合理的,也就是说,实际上,SCAN 可能会在迭代大集合时返回数十个元素的最大数量的元素,或者可能会将集合中的所有元素一次返回当迭代集合足够小以便在内部表示为编码数据结构(这发生在小集合,散列和有序集合)时调用。
但是,用户可以使用 COUNT
选项调整每次呼叫返回元素数量级的顺序。
COUNT选项
虽然 SCAN 不提供有关每次迭代返回的元素数量的保证,但可以使用 COUNT
选项凭经验调整 SCAN 的行为。基本上用
COUNT,用户指定了每次调用应该完成的工作量,以便从集合中检索元素
。这只是实现的一个暗示
,但是一般来说,这是您实施过程中大部分时间所期望的。
- COUNT 的默认值是10。
- 当迭代密钥空间,或者一个足够大的集合,哈希或排序集合,以便用哈希表表示时(假设没有使用
MATCH
选项),服务器通常会返回计数
或比每次调用的计数
元素多一点。
- 迭代编码为 intsets 的集合(由整数组成的小集合)或编码为 ziplists 的哈希和排序集(小哈希和由小个别值组成的集合)时,通常所有元素都在第一个 SCAN 调用中返回,而不管 COUNT 值。
重要提示:每次迭代都不需要使用相同的 COUNT 值
。调用者可以根据需要自由地将计数从一次迭代更改为另一次迭代,只要在下次调用中传递的游标是在先前对该命令的调用中获得的游标。
MATCH选项
只能迭代匹配给定全局样式模式的元素,类似于只将模式作为参数的 KEYS 命令的行为。
为此,只需MATCH <pattern>在 SCAN 命令末尾附加参数(它可与所有 SCAN 系列命令一起使用)。
这是一个使用 MATCH
进行迭代的例子:
redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
2) "feelsgood"
3) "foobar"
redis 127.0.0.1:6379>
重要的是要注意,在将数据返回给客户端之前,在从集合中检索元素之后应用 MATCH
过滤器。这意味着如果模式匹配集合中很少的元素,SCAN 很可能在大多数迭代中都不返回元素。一个例子如下所示:
redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"
2) "key:711"
3) "key:118"
4) "key:117"
5) "key:311"
6) "key:112"
7) "key:111"
8) "key:110"
9) "key:113"
10) "key:211"
11) "key:411"
12) "key:115"
13) "key:116"
14) "key:114"
15) "key:119"
16) "key:811"
17) "key:511"
18) "key:11"
redis 127.0.0.1:6379>
正如您所看到的,大部分调用都返回了零个元素,但是最后一次调用的 COUNT 为1000,以强制命令为该迭代执行更多扫描。
多重并行迭代
无限数量的客户端可能会同时迭代同一个集合,因为迭代器的完整状态在游标中,每次调用都会获取并返回给客户端。服务器端根本没有状态。
终止中间的迭代
由于没有状态服务器端,但全状态由光标捕获,因此调用者可以自由地在半途中终止一次迭代,而无需以任何方式将此信号通知给服务器。无限次的迭代可以开始,并且不会在没有任何问题的情况下终止。
使用损坏的光标调用SCAN
调用带有破损,负值,超范围或其他无效光标的 SCAN 将导致未定义的行为,但不会导致崩溃。未定义的是,对于返回元素的保证不能再由 SCAN 实现来保证。
唯一有效的游标是:
- 开始迭代时光标值为0。
- 前一次调用 SCAN 返回的光标为了继续迭代。
保证终止
SCAN 算法只有在迭代集合的大小保持限定为给定的最大大小时才被保证终止,否则迭代总是增长的集合可能导致 SCAN 永远不会终止完整迭代。
这很容易直观地看到:如果集合增长,为了访问所有可能的元素需要做更多的工作,并且终止迭代的能力取决于 SCAN 调用的次数和它的 COUNT 选项值,与收藏增长的速度。
返回值
SCAN,SSCAN,HSCAN 和 ZSCAN 返回一个双元素多批量回复,其中第一个元素是一个表示无符号64位数字(游标)的字符串,第二个元素是具有元素数组的多个批量。
- SCAN 数组元素是一个键列表。
- SSCAN 元素数组是 Set 成员的列表。
- 对于 Hash 的每个返回元素,HSCAN 元素数组包含两个元素,一个字段和一个值。
- 对于有序集合的每个返回元素,ZSCAN 元素数组包含两个元素,一个成员及其相关分数。
其他例子
迭代一个哈希值。
redis 127.0.0.1:6379> hmset hash name Jack age 33
OK
redis 127.0.0.1:6379> hscan hash 0
1) "0"
2) 1) "name"
2) "Jack"
3) "age"
4) "33"