Redis Scan命令的基本使用方法

 更新时间:2021年1月15日 13:19  点击:2483

1. 概述

SCAN 命令以及比较相近的 SSCAN、HSCAN 和 ZSCAN 命令都用于增量迭代数据集元素:

  • SCAN 命令用于迭代当前数据库中的数据库键。
  • SSCAN 命令用于迭代集合(Set)中的元素。
  • HSCAN 命令用于迭代哈希(Hash)中的字段以及对应的值。
  • ZSCAN 命令用于迭代有序集合(Sorted Set)中的元素以及对应的得分。

由于这些命令都可以增量迭代,每次调用都只会返回少量元素,所以这些命令可以用于生产环境中,不用担心像使用 KEYS、SMEMBERS 命令带来的问题。在键或元素的大数据集上调用这些命令可能会长时间(甚至几秒钟)阻塞服务器。像 SMEMBERS 这样的阻塞命令能够在给定的时间内提供数据集中所有的元素,但 SCAN 系列命令仅对返回的元素提供有限的保证,因为数据集在我们增量迭代时可能会发生改变。

SCAN,SSCAN,HSCAN 以及 ZSCAN 命令工作原理都非常类似,因此这篇文章会涵盖这四个命令。区别在于 SSCAN,HSCAN 以及 ZSCAN 命令,第一个参数是保存 Set,Hash或 Sorted Set 值的键的名称。SCAN命令不需要任何键名参数,因为它会迭代当前数据库中所有的键,因此迭代的对象是数据库本身。

2. 基本用法

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"

在上面的示例中,第一次调用使用 0 作为游标来开始一次新的迭代。第二次调用时使用上一次调用返回的游标,即命令回复的第一个元素值,即17。从上面的示例可以看到,SCAN 命令返回值是两个值的数组:第一个值是下一次调用中将要使用的新游标,第二个值是包含返回元素的数组。

由于在第二次调用中返回的游标为 0,因此服务器向调用者发送信号,告知迭代已完成,并且遍历完数据集。从游标值 0 开始迭代,然后调用 SCAN 直到返回的游标再次为 0,表示一个完整迭代。

3. 保证

SCAN 命令,以及其他增量迭代命令,在整个完整迭代过程中可以为用户提供一系列的保证:

  • 在完整迭代开始直到完整迭代结束期间内的所有元素都会被遍历返回;这意味着,如果某个给定元素在开始迭代时位于数据集内,并且在终止迭代时仍然存在,那么 SCAN 会在某次迭代时返回给用户。
  • 在完整迭代开始直到完整迭代结束期间内不存在的元素永远都不会被返回;因此,如果某个元素在迭代开始之前就被删除,并且在后续的迭代过程中从未添加回数据集中,那么 SCAN 永远都不会返回该元素 。

但是,由于 SCAN 只有很少的关联状态(仅有游标),因此具有以下缺点:

  • 同一个元素可能会被返回多次。重复元素的问题需要我们自己的应用程序处理, 例如,可以考虑将迭代返回的元素用于幂等操作(可以重复执行多次操作)上。
  • 如果一个元素是在迭代过程中被添加到数据集的,又或者是在迭代过程中从数据集中被删除的,那么这个元素可能会被返回,也可能不会。

4. 每次执行返回数量

SCAN 系列的函数不能保证每次调用返回的元素数量会在给定范围内。每次调用可能会返回 0 个元素,但只要返回的游标不为 0,客户端就认为迭代没有结束(即使返回了 0 个元素也不能表示迭代的结束)。返回的元素数量会符合一定的规则:

  • 在迭代大型数据集时,SCAN 最多可能会返回几十个元素。
  • 在迭代小的数据集并且内部为编码数据结构时(小的 Set、Hashe 以及 Sorted Set),单次调用就可以返回数据集的所有元素。

但是,用户可以使用 COUNT 参数来调整每次调用返回的元素的数量级。

5. COUNT参数

虽然 SCAN 不能保证每次迭代返回的元素数量,但是可以使用 COUNT 参数根据经验进行调整。基本上,COUNT 参数的作用就是让用户告知迭代命令,在每次迭代中应该从数据集里返回多少元素。虽然 COUNT 参数只是迭代命令实现上的一种提示(hint),但是在大多数情况下,这种提示是能满足我们的预期:

  • COUNT 默认值为 10。
  • 在迭代一个足够大、由哈希表实现的数据库、Set、Hash 或者 Sorted Set 时,如果用户没有使用 MATCH 参数,那么每次调用返回 COUNT 个元素,或者比 COUNT 稍多的元素。
  • 在迭代一个编码为 IntSet (一个只由整数值构成的小数据集) 或 Hash 的 Set 以及编码为 ZipList (由不同值构成的小的 Hash 或者 Set) 的 Sorted Set 时,通常会无视 COUNT 参数指定的值,并在第一次调用时就将数据集包含的所有元素都返回给用户。

没有必要每次迭代都要使用相同的 COUNT 值。用户可以在每次迭代中按自己的需要随意改变 COUNT 值,只要记得将上次迭代返回的游标用到下次迭代里面就可以了。

6. MATCH参数

我们也可以通过匹配一个 Glob 风格的模式来迭代元素,类似于 KEYS 命令。我们只需要在 SCAN 命令后面追加 MATCH <pattern> 参数即可实现。

以下是一个使用 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,强制命令对该迭代进行更多扫描,从而使得命令返回的元素也变多了。

7. TYPE参数

从 6.0 版开始,我们可以使用此参数要求 SCAN 命令仅返回与给定类型匹配的对象,从而允许我们遍历数据库以查找特定类型的键。SCAN 可以使用 TYPE 参数,但 HSCAN 或 ZSCAN 等不可用。

type 参数与 TYPE 命令返回的字符串名称相同。需要我们注意的是某些 Redis 类型(例如GeoHashes、HyperLogLogs、Bitmap 以及 Bitfields 等)其内部是使用其他 Redis 类型(例如 String 或 Zset)来实现的,因此 SCAN 命令无法将其与相同类型的其他键区分开。例如,ZSET 和 GEOHASH:

redis 127.0.0.1:6379> GEOADD geokey 0 0 value
(integer) 1
redis 127.0.0.1:6379> ZADD zkey 1000 value
(integer) 1
redis 127.0.0.1:6379> TYPE geokey
zset
redis 127.0.0.1:6379> TYPE zkey
zset
redis 127.0.0.1:6379> SCAN 0 TYPE zset
1) "0"
2) 1) "geokey"
 2) "zkey"

重要的是,TYPE 过滤器是在从数据库中检索元素之后应用的,因此该参数不会降低服务器完成完整迭代所需的负载,对于稀有类型,我们可能不会收到任何元素。

8. 多次并行迭代

不同客户端可能在同一时间迭代同一数据集,客户端每次执行迭代都需要传入一个游标,并在迭代结束之后获得一个新的游标,而这个游标就包含了迭代的所有状态,因此,服务器无须为迭代记录任何状态。

9. 在中间终止迭代

由于服务器端不会记录状态,迭代的所有状态都保存在游标中,因此调用方可以自由地中途终止迭代,不用向服务器发送通知。An infinite number of iterations can be started and never terminated without any issue.

10. 使用错误的游标调用SCAN

使用错误的,负数的,超出范围的游标或其他无效的游标来调用 SCAN,会导致未定义的行为,但绝不会导致崩溃。未定义的是指 SCAN 将不再确保返回元素的保证。

唯一有效的游标是:

开始迭代时的游标值为0。

上一次调用 SCAN 返回的游标,以便继续迭代。

11. 终止保证

只有在保证迭代的数据集大小始终保持在给定的最大上限内时(大小恒定),才能保证 SCAN 算法能终止;否则,对一直增长的数据集进行迭代可能会导致 SCAN 永远不会终止迭代(死循环)。

这很容易直观地看出:如果数据集不断增长,为了访问所有可能出现的元素,将需要做越来越多的工作,而能否结束一个迭代取决于对 SCAN 的调用次数、COUNT 参数值以及数据集的增长速度。

12. 返回值

SCAN,SSCAN,HSCAN 以及 ZSCAN 命令都返回一个包含两个元素的回复,第一个元素表示游标的无符号64位整数,第二个元素是迭代出的元素数组:

SCAN 元素数组是键的列表。

SSCAN 元素数组是 Set 成员的列表。

HSCAN 元素数组包含两个元素,即字段和值,对应 Hash 的每个返回元素。

ZSCAN 元素数组包含两个元素,即一个成员及其关联的分数,对应 Sorted Set 中的每个返回元素。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对猪先飞的支持。

[!--infotagslink--]

相关文章

  • 详解如何清理redis集群的所有数据

    这篇文章主要介绍了详解如何清理redis集群的所有数据,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-18
  • Redis连接池配置及初始化实现

    这篇文章主要介绍了Redis连接池配置及初始化实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-29
  • 详解redis desktop manager安装及连接方式

    这篇文章主要介绍了redis desktop manager安装及连接方式,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-15
  • 使用percona-toolkit操作MySQL的实用命令小结

    1.pt-archiver 功能介绍: 将mysql数据库中表的记录归档到另外一个表或者文件 用法介绍: pt-archiver [OPTION...] --source DSN --where WHERE 这个工具只是归档旧的数据,不会对线上数据的OLTP查询造成太大影响,你可以将...2015-11-24
  • 浅谈redis key值内存消耗以及性能影响

    这篇文章主要介绍了浅谈redis key值内存消耗以及性能影响,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-07
  • lua读取redis数据的null判断示例代码

    最近在工作中遇到了一个问题,通过查找相关资料才得知原因是因为返回结果的问题,下面这篇文章主要给大家介绍了关于lua读取redis数据的null判断的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下...2020-06-30
  • Linux中grep命令详解

    grep命令是Linux系统中最重要的命令之一,功能是从文本文件或管道数据流中筛选匹配的行和数据,如果再配合正则表达式,功能十分强大,是Linux运维人员必备的命令,这篇文章主要介绍了Linux中grep详解,需要的朋友可以参考下...2023-02-15
  • SpringBoot集成Redis实现消息队列的方法

    这篇文章主要介绍了SpringBoot集成Redis实现消息队列的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-10
  • redis setIfAbsent和setnx的区别与使用说明

    这篇文章主要介绍了redis setIfAbsent和setnx的区别与使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-04
  • C#隐式运行CMD命令(隐藏命令窗口)

    这篇文章主要介绍了C#隐式运行CMD命令(隐藏命令窗口),本文实现在winform窗口中运行CMD命令,需要的朋友可以参考下...2020-06-25
  • PHP实现连接设备、通讯和发送命令的方法

    本文实例讲述了PHP实现连接设备、通讯和发送命令的方法。分享给大家供大家参考。具体如下:开发的BS架构的软件(PHP),需要跟设备进行通讯,在此记录一下,欢迎各位指正:1. 采用php socket技术使用TCP/IP连接设备参数$service_po...2015-10-21
  • Redis的Expire与Setex区别说明

    这篇文章主要介绍了Redis的Expire与Setex区别说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-15
  • 对MySQL日志操作的一些基本命令总结

    MySQL日志主要包含:错误日志、查询日志、慢查询日志、事务日志、二进制日志;日志是mysql数据库的重要组成部分。日志文件中记录着mysql数据库运行期间发生的变化;也就是说用来记录mysql数据库的客户端连接状况、SQL语句...2015-11-24
  • Mysql命令大全(详细篇)

    一、连接Mysql格式: mysql -h主机地址 -u用户名 -p用户密码1、连接到本机上的MYSQL。首先打开DOS窗口,然后进入目录mysql/bin,再键入命令mysql -u root -p,回车后提示你输密码.注意用户名前可以有空格也可以没有空格,但是密...2015-11-08
  • 查看Redis内存信息的命令

    Redis 是一个开源、高性能的Key-Value数据库,被广泛应用在服务器各种场景中。本文介绍几个查看Redis内存信息的命令,包括常用的info memory、info keyspace、bigkeys等。...2021-01-15
  • 解决jmap命令打印JVM堆信息异常的问题

    这篇文章主要介绍了解决jmap命令打印JVM堆信息异常的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-04
  • Redis的持久化方案详解

    在本篇文章里小编给大家整理的是关于Redis的持久化方案详解,有兴趣的朋友们可以参考下。...2021-01-15
  • @CacheEvict + redis实现批量删除缓存

    这篇文章主要介绍了@CacheEvict + redis实现批量删除缓存方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-12
  • JAVA中 redisTemplate 和 jedis的配合使用操作

    这篇文章主要介绍了JAVA中 redisTemplate 和 jedis的配合使用操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-13
  • MySQL的常用命令集锦

    下面是我们经常会用到且非常有用的MySQL命令。下面你看到#表示在Unix命令行下执行命令,看到mysql>表示当前已经登录MySQL服务器,是在mysql客户端执行mysql命令。 登录MySQL,如果连接远程数据库,需要用-h指定hostname。登...2015-11-24