Redis数据库系列之二

Scan是Redis2.8版本后提供了一个新的命令,它能有效的解决keys命令存在的问题。和keys命令执行时会遍历所有键不同,scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是O(1),但是要真正实现keys的功能,需要执行多次scan。

目录

scan


scan

1.语法
scan cursor [match pattern][count count]
2.解析
scan命令及其相关的sscan命令、hscan命令和zscan命令都用于增量地迭代-集元素:
scan:命令用于迭代当前数据库中的数据库键.
sscan:命令用于迭代集合键中的元素。
hscan:命令用于迭代哈希键中的键值对。
zscan:命令用于迭代有序集合中的元素。(包括元素成员和元素分值)
以上四个命令都支持增量式迭代,他们每次执行都只会返回少量元素,所以这些命令可以用于生产环境,而不会出现像keys命令、smember命令会带来的问题--当keys命令被用于处理一个大的数据库时,又或者smembers命令被用于处理一个大的集合键时,他们可能会阻塞服务器达数秒之久。
不过,增量式迭代命令也不是没有缺点的:
举个例子:使用smembers命令可以返回集合键当前包含的所有元素,但是对于scan这类增量迭代命令来说,因为在键进行增量式迭代的过程中,键可能会被修改,所以增量迭代命令只能对被返回的元素提供有限的保证。
因为scan、sscan、hscan和zscan四个命令的工作方式都非常的相似,所以一并介绍:
sscan命令、hscan命令和zscan命令的第一个参数总是一个数据库键,而scan命令则不需要在第一个参数提供任何数据键--因为它迭代的是当前数据库中的所有数据库键。
3.scan命令的基本用法
scan命令是一个基于游标的迭代器:scan每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为scan命令的游标参数,
以此来延续之前的迭代过程。
当scan命令的游标参数被设置为0时,服务器将开始一次新的迭代,而当服务器向用户返回值为0的游标时,表示迭代结束。
3.1)例子:

mset a 1 b 2 c 3 d 4 e 5 f 6 g 7 h 8 i 9 j 10 k 11 l 12
scan 0
scan 7

3.2)解析:
在上面这个例子中,第一次迭代使用0作为游标,表示开始一次新的迭代。
第二次迭代使用的是第一次迭代时返回的游标,也即是命令回复第一个元素的值--7.
从上面的示例可以看到,scan命令的回复是一个包含两个元素的数组,第一个元素是用于进行下一次迭代的游标,而第二个元素是一个数组,这个数组包含了所有被迭代的元素。
在第二次调用scan命令时,命令返回了游标0,这表示迭代已经结束,整个数据集已经被完整遍历过了。
以0作为游标开始一次新的迭代,一直调用scan命令,直到命令返回游标0,我们称这个过程为一次完整遍历.
4.scan命令的保证
scan命令,以及其他增量式迭代命令,在进行完整遍历的情况下可以给用户带来以下保证:
从完整遍历开始直到完整遍历结束期间,一直存在于数据集内的所有元素都会被完整遍历返回;
这意味着,如果有一个元素,它从遍历开始直到遍历结束期间都存在与被遍历的数据集中,那么scan命令总会在某次迭代中将这个元素返回给用户。
然而因为增量式命令仅仅使用游标来记录迭代状态,所以这些命令带有一下缺点
同一个元素可能被返回多次,处理重复元素的工作交由应用程序负责.比如说,可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。
如果一个元素是在迭代过程中被添加到数据集的,又或者是在迭代过程中从数据集被删除的,那么这个元素可能会被返回,也可能不会,这是未定义的。
5.scan命令每次执行返回的元素数量
增量式迭代命令并不保证每次执行都返回某个给定数量的元素。
增量式命令设置可能返回零个元素,但只要命令返回的游标不是0,应用程序就不应该将迭代视作结束。
不过命令返回的元素数量总是符合一定规则的,在实际中:
对于一个大数据集来说,增量式迭代命令每次可能会返回数十个元素;
对于一个足够小的数据集来说,如果这个数据集的底层表示为编码数据结构(适用于是小集合键,小哈希键和小有序集合键),
那么增量迭代命令将在一次调用中返回数据集中的所有元素。
最后,用户可以通过增量迭代式命令提供count选项来指定每次迭代返回元素的最大值。
6.count选项
虽然增量式迭代命令不保证每次迭代返回的元素数量,但我们可以使用count选项,对命令进行一定程度上的调整。
虽然count选项只是对增量式迭代命令的一种提示,但是在大多数情况下,这种提示都是有效的。
count默认值为10.
在迭代一个足够大的,有哈希表实现的数据库,集合键,哈希键或者有序集合键时,如果用户没有使用match选项,
那么命令返回的元素数量通常和count选项指定的一样,或者比count选项指定的数量稍多一些。
在迭代一个编码为整数集合、或者编码为压缩列表时,增量式迭代命令通常会无视count选项指定的值,在第一次迭代就将数据集包含的所有元素都返回给用户。
:并非每次迭代都用相同的count值。
用户可以在每次迭代中按自己的需要随意改变count值,只要记得上次迭代返回的游标用到下次迭代里面就可以了。
7.match选项:
和keys命令一样,增量式迭代命令也可以通过提供一个glob风格的模式参数,让命令只返回和给定模式匹配的元素,
这一点可以通过在执行增量迭代命令时,通过给定match参数来实现。
7.1)例子:

sadd myset  1 2 3 foo foobar feelsgood
sscan myset 0 match f*

需要注意的是,对元素的模式匹配工作是在命令从数据集中取出元素之后,向客户端返回元素之前的这段时间内进行的,
所以如果被迭代的数据集中只有少量元素和模式相匹配,那么迭代命令或许会在多次执行中都不返回任何元素。
7.2)例子:

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"

以上大部分迭代都不返回任何元素。
在最后一次迭代,我们通过将count选项的参数设置为1000,强制命令为本次迭代扫描更多元素,从而使得命令返回的元素也变多了。
8.并发执行多个迭代
在同一时间,可以有任意多个客户端对同一数据集进行迭代,客户端每次执行迭代都需要传入一个游标,并在迭代执行之后获得一个新的游标,而这个游标就包含了迭代的所有状态,因此,服务器无需为迭代记录任何状态。
9.中途停止迭代
因为迭代的所有状态都保存在游标里面,而服务器无需为迭代保存任何状态,所以客户端可以在中途停止一个迭代,而无需对服务器进行任何通知。
10.使用错误的游标进行增量式迭代
使用简单的、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃,但可能会让命令产生未定义的行为.
未定义行为指的是,增量式命令对返回值所做的保证可能会不再为真。
10.1)只有两种游标是合法的:
1.在一个新的迭代时,游标必须为0.
2.增量式迭代命令在执行时候返回的,用于延续迭代过程的游标。
11.迭代终结的保证
增量式迭代命令所使用的算法只保证在数据集的大小有界的情况下,迭代才会停止,换句话说,如果被迭代数据集的大小不断地增长的话,增量式迭代命令可能永远也无法完成一次完整的迭代。
从直觉上可以看出,当一个数据集不断地变大时,想要访问这个数据集中的所有元素就需要做越来越多的工作,能否结束一个迭代取决于用户执行迭代的速度是否比数据集增长的速度更快.
12.返回值
scan命令、sscan命令、hscan命令和zscan命令都会返回一个包含两个元素的multi-bulk回复:
回复的第一个元素是字符串表示的无符号64位整数(游标),回复的第二个元素是另一个multi-bulk回复,这个multi-bulk回复包含本次被迭代的元素
scan命令返回的每个元素都是一个数据库键。
sscan命令返回的每个元素都是一个集合。
hscan命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
zscan命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员和一个分值组成。
13.例子

#scan(数据库键)
mset google "google.cn"   baidu  "baidu.com"
scan 0 match *g*
#hscan(键中的键值对)
hset student name sunny age 30 sex 1
hscan student 0 match *ex*
#sscan(集合键中的元素)
sadd baidu "baidu.com"  "baidu.cn"
sscan baidu 0
#zscan(集合中的成员和分值)
zadd bbs 10 "bbs.com" 24 "bbs.cn" 9 "bbs.gov"
zscan bbs 0 match *gov*

 

你可能感兴趣的:(Redis)