Redis SCAN 命令用于增量地迭代 Redis 元素集合。
SCAN 命令是一个基于游标的迭代器,SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。
当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代元素集。
SCAN、 SSCAN、 HSCAN 和 ZSCAN 四个命令的工作方式都非常相似:
SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一个参数总是一个数据库键。
而 SCAN 命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。
192.168.98.70:6379> SCAN CURSOR [MATCH pattern] [COUNT count]
虽然增量式迭代命令不保证每次迭代所返回的元素数量, 但我们可以使用 COUNT 选项, 对命令的行为进行一定程度上的调整。
基本上, COUNT 选项的作用就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素。
虽然 COUNT 选项只是对增量式迭代命令的一种提示(hint), 但是在大多数情况下, 这种提示都是有效的。
COUNT 参数的默认值为 10 。
在迭代一个足够大的、由哈希表实现的数据库、集合键、哈希键或者有序集合键时, 如果用户没有使用 MATCH 选项, 那么命令返回的元素数量通常和 COUNT 选项指定的一样, 或者比 COUNT 选项指定的数量稍多一些。
在迭代一个编码为整数集合(intset,一个只由整数值构成的小集合)、 或者编码为压缩列表(ziplist,由不同值构成的一个小哈希或者一个小有序集合)时, 增量式迭代命令通常会无视 COUNT 选项指定的值, 在第一次迭代就将数据集包含的所有元素都返回给用户。
和 KEYS 命令一样, 增量式迭代命令也可以通过提供一个 glob 风格的模式参数, 让命令只返回和给定模式相匹配的元素, 这一点可以通过在执行增量式迭代命令时, 通过给定 MATCH <pattern>
参数来实现。
SCAN、 SSCAN、 HSCAN 和 ZSCAN 命令都返回两个元素
SCAN 命令返回的每个元素都是一个数据库键。
SSCAN 命令返回的每个元素都是一个集合成员。
HSCAN 命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
ZSCAN 命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。
增量式迭代命令并不保证每次执行都返回某个给定数量的元素。
增量式命令甚至可能会返回零个元素, 但只要命令返回的游标不是 0 , 应用程序就不应该将迭代视作结束。
不过命令返回的元素数量总是符合一定规则的, 在实际中:
最后, 用户可以通过增量式迭代命令提供的 COUNT 选项来指定每次迭代返回元素的最大值。
增量式迭代命令每次执行的复杂度为 O(1) , 对数据集进行一次完整迭代的复杂度为 O(N) , 其中 N 为数据集中的元素数量。
>= 2.8.0
SCAN 命令, 以及其他增量式迭代命令, 在进行完整遍历的情况下可以为用户带来以下保证:
然而因为增量式命令仅仅使用游标来记录迭代状态, 所以这些命令带有以下缺点:
同一个元素可能会被返回多次。 处理重复元素的工作交由应用程序负责, 比如说, 可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。
如果一个元素是在迭代过程中被添加到数据集的, 又或者是在迭代过程中从数据集中被删除的, 那么这个元素可能会被返回, 也可能不会, 这是未定义的(undefined)。
在同一时间, 可以有任意多个客户端对同一数据集进行迭代。
客户端每次执行迭代都需要传入一个游标, 并在迭代执行之后获得一个新的游标, 而这个游标就包含了迭代的所有状态, 因此, 服务器无须为迭代记录任何状态。
因为迭代的所有状态都保存在游标里面, 而服务器无须为迭代保存任何状态, 所以客户端可以在中途停止一个迭代, 而无须对服务器进行任何通知。
即使有任意数量的迭代在中途停止, 也不会产生任何问题。
使用间断的(broken)、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃, 但可能会让命令产生未定义的行为。
未定义行为指的是, 增量式命令对返回值所做的保证可能会不再为真。
只有两种游标是合法的:
在开始一个新的迭代时, 游标必须为 0 。
增量式迭代命令在执行之后返回的, 用于延续(continue)迭代过程的游标。
增量式迭代命令所使用的算法只保证在数据集的大小有限的情况下, 迭代才会停止, 换句话说, 如果被迭代数据集的大小不断地增长的话, 增量式迭代命令可能永远也无法完成一次完整迭代。
所以, 当一个数据集不断地变大时, 想要访问这个数据集中的所有元素就需要做越来越多的工作, 能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快。
使用 SCAN 开始迭代
# 基本迭代
192.168.98.70:6379> scan 0
1) "21"
2) 1) "key:1"
2) "key:8"
3) "key:4"
4) "key:7"
5) "key:16"
6) "key:15"
7) "key:17"
8) "key:10"
9) "key:3"
10) "key:14"
11) "key:12"
192.168.98.70:6379> scan 21
1) "0"
2) 1) "key:9"
2) "key:18"
3) "key:0"
4) "key:19"
5) "key:13"
6) "key:2"
7) "key:6"
8) "key:5"
9) "key:11"
第一次迭代使用 0 作为游标, 表示开始一次新的迭代。第二次迭代使用的是第一次迭代时返回的游标, 也即是命令回复第一个元素的值 21 。
从上面的示例可以看到,SCAN 命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素。
在第二次调用 SCAN 命令时, 命令返回了游标 0 , 这表示迭代已经结束, 整个数据集(collection)已经被完整遍历过了。
以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整迭代。
使用 MATCH 参数开始迭代
# MATCH 参数迭代
192.168.98.70:6379> SADD www.haicoder.net Redis MongoDb Mysql Java Php GoLang HTML CSS Vue Js
(integer) 10
192.168.98.70:6379> SSCAN www.haicoder.net 0 MATCH M*
1) "0"
2) 1) "Mysql"
2) "MongoDb"
192.168.98.70:6379> DEL www.haicoder.net
(integer) 1
我们首先,使用 SADD 命令向 SET 添加多个元素。接着,使用 SSCAN 命令加上 MATCH 参数,匹配集合中以 M 开头的元素 。游标返回了 0,表示迭代结束,并且返回了 以 M 开头的元素 。
需要注意的是, 对元素的模式匹配工作是在命令从数据集中取出元素之后, 向客户端返回元素之前的这段时间内进行的, 所以如果被迭代的数据集中只有少量元素和模式相匹配, 那么迭代命令或许会在多次执行中都不返回任何元素,但此时迭代并没有结束。
Redis SCAN 命令用于增量地迭代 Redis 元素集合。Redis SCAN 命令语法:
192.168.98.70:6379> SCAN CURSOR [MATCH pattern] [COUNT count]