Redis查找指令——SCAN

目录

 

1、keys键命令缺点

2、引入scan命令

3、scan使用

4、更多scan指令

5、允许中途停止迭代

6、迭代什么时候终结

7、时间复杂度


1、keys键命令缺点

    Redis 提供了一个简单暴力的指令 keys 用来列出所有满足特定正则字符串规则的key。keys指令使用非常简单,提供一个简单的正则字符串即可,但是有很明显的两个缺点:
    1)没有 offset、limit 参数,一次性吐出所有满足条件的 key,如果实例中有几百万个key满足条件,那就可能导致满屏的字符串刷的没有尽头;
    2)keys算法是遍历算法,复杂度是 O(n),如果实例中有千万级以上的 key,这个指令就会导致Redis服务卡顿,甚至造成阻塞,因为Redis是单线程程序,顺序执行所有指令,其它指令必须等到当前的 keys 指令执行完了才可以继续。
    所以,生产环境一般是屏蔽keys命令的。

2、引入scan命令

    SCAN命令是一个基于游标的迭代器。这里的意思是:命令每次被调用都需要使用上一次该调用返回的游标作为本次调用的游标参数,以此来延续之前的迭代过程。当设置SCAN命令的游标参数为0时,服务器将开始一次新的迭代;而当redis服务器向用户返回值为0的游标时,表示迭代已结束,这是唯一迭代结束的判定方式,而不能通过返回结果集是否为空判断迭代结束
scan相比keys具备有以下优点:
    1)复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
    2)提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是对增量式迭代命令的一种提示(hint),返回的结果可多可少;
    3)服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
------------
scan也有缺点:
    1)返回的结果可能会有重复,需要客户端去重复,这点非常重要;
    2)遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的。即如果一个元素是在迭代过程中被添加到数据集的, 又或者是在迭代过程中从数据集中被删除的, 那么这个元素可能会被返回, 也可能不会, 这是未定义未知的。
    3)单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零
 

3、scan使用

redis-cli scan [cursor] match [pattern] count [limit]
1、提供3个参数:
  1. cursor整数值  用户将游标设置为0,表示开始新一次的迭代
  2. pattern正则表达式
  3. count limit 
    1. 默认10 
    2. 注意这里的limit并不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数)
    3. 注意并非每次迭代都要使用相同的 COUNT 值。用户可以在每次迭代中按自己的需要随意改变 COUNT 值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了
    4. 使用了错误的游标。使用间断的(broken)、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃, 但可能会让命令产生未定义的行为。
    5. 游标的合法值只有2个:
      1. 在开始一个新的迭代时, 游标必须为0;
      2. 使用前一次迭代命令返回的迭代游标值。
2、返回值:
  1. 下一次迭代游标
  2. 本次迭代结果集(有可能为空)
3、scan过程:
    第一次遍历时,cursor 值为0,后续将返回结果中第一个整数值作为下一次遍历的cursor。一直遍历到redis返回的游标值为0时结束。
    以0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历。
 

4、scan指令实例:

$ redis-cli scan 0 match key99* count 1000
1) "13912"
2)  1) "key997"
    2) "key9906"
    3) "key9957"
    4) "key9902"
    5) "key9971"
    6) "key9935"
    7) "key9958"
    8) "key9928"
    9) "key9931"
   10) "key9961"
   11) "key9948"
   12) "key9965"
   13) "key9937"
   
$ redis-cli scan 13912 match key99* count 1000
1) "5292"
2)  1) "key996"
    2) "key9960"
    3) "key9973"
    4) "key9978"
    5) "key9927"
    6) "key995"
    7) "key9992"
    
   从上面的过程可以看到虽然设置的limit是1000,但是返回的结果只有 10 个左右。这是因为因为这个 limit 不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数量(约等于)。所以如果将limit 设置为 10,你会发现返回结果是空的,但是游标值不为零,意味着遍历还没结束。

如果将limit设置为10,例如下:
$ redis-cli scan 0 match key99* count 10
1) "15360"
2) (empty list or set)

$ redis-cli scan 15360 match key99* count 10
1) "2304"
2) (empty list or set)

4、更多scan指令

scan 指令是一系列指令,除了可以遍历所有的 key 之外,还可以对指定的容器集合进行遍历。’

  • SCAN 命令用于迭代当前数据库中的数据库键,
  • zscan 遍历 zset 集合元素,
  • hscan 遍历 hash 字典的键值对,
  • sscan 遍历 set 集合的元素。

    注意,SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一个参数总是一个数据库键。而 SCAN 命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。

5、允许中途停止迭代

    因为迭代的所有状态都保存在游标里面, 而服务器无须为迭代保存任何状态, 所以客户端可以在中途停止一个迭代, 而无须对服务器进行任何通知。即使有任意数量的迭代在中途停止, 也不会产生任何问题。

6、迭代什么时候终结

    当redis服务器向用户返回值为0的游标时,表示迭代已结束,这是唯一迭代结束的判定方式,而不能通过返回结果集是否为空判断迭代结束。

    同时增量式迭代命令所使用的算法只保证在数据集的大小有界(bounded)的情况下, 迭代才会停止, 换句话说, 如果被迭代数据集的大小不断地增长的话, 增量式迭代命令可能永远也无法完成一次完整迭代。

7、时间复杂度

    每次执行的复杂度为 O(1),对数据集进行一次完整迭代的复杂度为 O(N),其中 N 为数据集中的元素数量。

8、参考资料

http://jinguoxing.github.io/redis/2018/09/04/redis-scan/

http://doc.redisfans.com/key/scan.html

发布了48 篇原创文章 · 获赞 35 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/kqZhu/article/details/104408644