一、概述
语法:
SCAN cursor [MATCH pattern] [COUNT count]
- cursor - 游标。
- pattern - 匹配的模式。
- count - 指定从数据集里返回多少元素,默认值为 10 。
起始版本:2.8.0
时间复杂度:O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.
(中文,考虑到有些靓仔英文看不懂!我反正看不懂)O(1)表示每个调用。O(N)表示一个完整的迭代,包括足够的命令调用,使光标返回到0。N是集合内的元素数。
- SCAN 命令用于迭代当前数据库中的key集合。
- SSCAN 命令用于迭代SET集合中的元素。
- HSCAN 命令用于迭代Hash类型中的键值对。
- ZSCAN 命令用于迭代SortSet集合中的元素和元素对应的分值
以上列出的四个命令都支持增量式迭代,它们每次执行都只会返回少量元素,所以这些命令可以用于生产环境,而不会出现像 EYS 或者 SMEMBERS 命令带来的可能会阻塞服务器的问题。
不过,SMEMBERS 命令可以返回集合键当前包含的所有元素, 但是对于SCAN这类增量式迭代命令来说,有可能在增量迭代过程中,集合元素被修改,对返回值无法提供完全准确的保证。
因为 SCAN, SSCAN, HSCAN 和 ZSCAN 四个命令的工作方式都非常相似, 所以这个文档会一并介绍这四个命令,需要注意的是SSCAN, HSCAN ,ZSCAN命令的第一个参数总是一个key; SCAN 命令则不需要在第一个参数提供任何key,因为它迭代的是当前数据库中的所有key。
说明:SSCAN,HSCAN,ZSCAN是针对于SET,HASH,SortSet类型的一个scan扩展,用法和scan一样
所以掌握的SCAN和用法,那么它下面的扩展命令还不是收到擒来
二、SCAN命令基本用法
SCAN命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次这个调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程当SCAN命令的游标参数被设置为 0 时
, 服务器将开始一次新的迭代
, 而当服务器向用户返回值为 0 的游标时
, 表示迭代已结束
。
案例:
127.0.0.1:6379> flushall OK 127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 k4 v4 k5 v5 k6 v6 k7 v7 k8 v8 k9 v9 k10 v10 k11 v11 #添加key和值 OK 127.0.0.1:6379> mset k12 v12 k13 v13 k14 v14 k15 v15 k16 v16 k17 v17 k18 v18 k19 v19 k20 v20 OK 127.0.0.1:6379> scan 0 #开始第一次遍历,游标为 0 表示开始新的遍历 1) "14" 2) 1) "k8" 2) "k11" 3) "k2" 4) "k19" 5) "k14" 6) "k13" 7) "k4" 8) "k10" 9) "k15" 10) "k1" 127.0.0.1:6379> scan 14 #继续接着上一次遍历,游标设置上一次返回数组的第一个元素值 14 1) "15" 2) 1) "k6" 2) "k5" 3) "k16" 4) "k3" 5) "k12" 6) "k7" 7) "k18" 8) "k9" 9) "k20" 10) "k17" 127.0.0.1:6379> scan 15 #当返回数组的第一个元素值时0的时候表示集合遍历完毕 1) "0" 2) (empty list or set) 127.0.0.1:6379>
在上面这个例子中, 第一次迭代使用 0 作为游标, 表示开始一次新的迭代。第二次迭代使用的是第一次迭代时返回的游标 14 ,作为新的迭代参数 。显而易见,SCAN命令的返回值 是一个包含两个元素的数组在第三次调用 SCAN 命令时, 命令返回了游标 0 , 这表示迭代已经结束, 整个数据集已经被完整遍历过了。
full iteration :以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历。
2.1 SCAN命令的保证
SCAN命令以及其他增量式迭代命令, 在进行完整遍历的情况下可以为用户带来以下保证:
- 会遍历到集合中的每一个元素(在遍历期间):从完整遍历开始直到完整遍历结束期间, 一直存在于数据集内的所有元素都会被完整遍历返回; 这意味着, 如果有一个元素, 它从遍历开始直到遍历结束期间都存在于被遍历的数据集当中, 那么 SCAN 命令总会在某次迭代中将这个元素返回给用户。
- 不存在集合中的元素不会遍历出来(在遍历期间):同样,如果一个元素在开始遍历之前被移出集合,并且在遍历开始直到遍历结束期间都没有再加入,那么在遍历返回的元素集中就不会出现该元素。
然而因为增量式命令仅仅使用游标来记录迭代状态, 所以这些命令带有以下缺点:
- 从集合中遍历出的元素可能会重复:同一个元素可能会被返回多次。 处理重复元素的工作交由应用程序负责, 比如说, 可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。
- 遍历期间添加或删除集合数据时,在这些数据也可能会被返回:如果一个元素是在迭代过程中被添加到数据集的, 又或者是在迭代过程中从数据集中被删除的, 那么这个元素可能会被返回, 也可能不会。
2.2 SCAN命令每次执行返回的元素数量
SCAN增量式迭代命令并不保证每次执行都返回某个给定数量的元素,甚至可能会返回零个元素, 但只要命令返回的游标不是 0 , 应用程序就不应该将迭代视作结束。
不过命令返回的元素数量总是符合一定规则的, 对于一个大数据集来说, 增量式迭代命令每次最多可能会返回数十个元素;而对于一个足够小的数据集来说, 如果这个数据集的底层表示为编码数据结构(小的sets, hashes and sorted sets), 那么增量迭代命令将在一次调用中返回数据集中的所有元素。
如果需要的话,用户可以通过增量式迭代命令提供的COUNT选项来指定每次迭代返回元素的最大值。
2.3 COUNT选项
对于增量式迭代命令不保证每次迭代所返回的元素数量,我们可以使用COUNT选项, 对命令的行为进行一定程度上的调整。COUNT 选项的作用就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素。使用COUNT 选项对于对增量式迭代命令相当于一种提示, 大多数情况下这种提示都比较有效的控制了返回值的数量。
- COUNT 参数的默认值为 10 。
- 数据集比较大时,如果没有使用MATCH 选项, 那么命令返回的元素数量通常和 COUNT 选项指定的一样, 或者比 COUNT 选项指定的数量稍多一些。
- 在迭代一个编码为整数集合(intset,一个只由整数值构成的小集合)、 或者编码为压缩列表(ziplist,由不同值构成的一个小哈希或者一个小有序集合)时, 增量式迭代命令通常会无视 COUNT 选项指定的值, 在第一次迭代就将数据集包含的所有元素都返回给用户。
注意: 并非每次迭代都要使用相同的 COUNT 值,用户可以在每次迭代中按自己的需要随意改变 COUNT 值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。
2.4 MATCH 选项
类似于KEYS 命令,增量式迭代命令通过给定 MATCH 参数的方式实现了通过提供一个 glob 风格的模式参数, 让命令只返回和给定模式相匹配的元素。
以下是一个使用 MATCH选项进行迭代的示例:
127.0.0.1:6379> mset m1 n1 m2 n2 m3 n3 OK 127.0.0.1:6379> keys * #查看所有key 1) "k8" 2) "k5" 3) "k14" 4) "k18" 5) "k10" 6) "k15" 7) "m1" 8) "k4" 9) "m2" 10) "k11" 11) "k16" 12) "k13" 13) "k9" 14) "k19" 15) "k3" 16) "k12" 17) "m3" 18) "k1" 19) "k20" 20) "k17" 21) "k2" 22) "k7" 23) "k6" 127.0.0.1:6379> scan 0 match m* #按照特定规则的方式遍历 1) "22" 2) 1) "m1" 127.0.0.1:6379> scan 22 match m* 1) "19" 2) 1) "m3" 2) "m2" 127.0.0.1:6379> scan 19 match m* 1) "0" 2) (empty list or set) 127.0.0.1:6379>
MATCH功能对元素的模式匹配工作是在命令从数据集中取出元素后和向客户端返回元素前的这段时间内进行的, 所以如果被迭代的数据集中只有少量元素和模式相匹配, 那么迭代命令或许会在多次执行中都不返回任何元素。
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 , 强制命令为本次迭代扫描更多元素, 从而使得命令返回的元素也变多了。
2.5 并发执行多个迭代
在同一时间, 可以有任意多个客户端对同一数据集进行迭代, 客户端每次执行迭代都需要传入一个游标, 并在迭代执行之后获得一个新的游标, 而这个游标就包含了迭代的所有状态, 因此, 服务器无须为迭代记录任何状态。
2.6 中止迭代
因为迭代的所有状态都保存在游标里面, 而服务器无须为迭代保存任何状态, 所以客户端可以在中途停止一个迭代, 而无须对服务器进行任何通知。即使有任意数量的迭代在中途停止, 也不会产生任何问题。
2.7 使用错误的游标
使用SCAN 命令传入间断的(broken)、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃, 但可能会让命令产生未定义的行为。未定义行为指的是, 增量式命令对返回值所做的保证可能会不再为真。 只有两种游标是合法的:
在开始一个新的迭代时, 游标必须为 0 。
增量式迭代命令在执行之后返回的, 用于延续迭代过程的游标。
2.8 迭代能终止的前提
增量式迭代命令所使用的算法只保证在数据集的大小有界的情况下, 迭代才会停止, 换句话说, 如果被迭代数据集的大小不断地增长的话, 增量式迭代命令可能永远也无法完成一次完整迭代。
从直觉上可以看出, 当一个数据集不断地变大时, 想要访问这个数据集中的所有元素就需要做越来越多的工作, 能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快。
2.9 返回值
SCAN, SSCAN, HSCAN 和 ZSCAN 命令都返回一个包含两个元素的 multi-bulk 回复: 回复的第一个元素是字符串表示的无符号 64 位整数(游标), 回复的第二个元素是另一个 multi-bulk 回复, 包含了本次被迭代的元素。
SCAN 命令返回的每个元素都是一个key。
SSCAN 命令返回的每个元素都是一个集合成员。
HSCAN 命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
ZSCAN命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。
2.10 另外的例子
迭代hash中的键值对:
127.0.0.1:6379> hmset vipclient name j3_liuliang age 18 #添加hash类型数据 OK 127.0.0.1:6379> hscan vipclient 0 #开始迭代 1) "0" 2) 1) "name" 2) "j3_liuliang" 3) "age" 4) "18" 127.0.0.1:6379>
结束语
- 本文结合Redis中文网和博主的实践案例及理解所写,算是一种扩展把对Redis的命令
- 由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。
- 如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。
- 感谢您的阅读,十分欢迎并感谢您的关注。