Redis遍历所有key的两个命令(KEYS 和 SCAN)


Posted in Redis onApril 12, 2021

当我们需要遍历Redis所有key或者指定模式的key时,首先想到的是KEYS命令:

KEYS pattern

官网对于KEYS命令有一个提示:  KEYS 的速度非常快,例如,Redis在一个有1百万个key的数据库里面执行一次查询需要的时间是40毫秒 。但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的  KEYS , 你最好还是用 Redis 的集合结构  SETS  来代替。
KEYS命令使用很简单。

redis> MSET one 1 two 2 three 3 four 4
OK
redis> KEYS *o*
1) "four"
2) "one"
3) "two"
redis> KEYS t??
1) "two"
redis> KEYS *
1) "four"
2) "three"
3) "one"
4) "two"
redis>

但由于KEYS命令一次性返回所有匹配的key,所以,当redis中的key非常多时,对于内存的消耗和redis服务器都是一个隐患,
对于Redis 2.8以上版本给我们提供了一个更好的遍历key的命令 SCAN 该命令的基本格式:

SCAN cursor [MATCH pattern] [COUNT count]

SCAN  每次执行都只会返回少量元素,所以可以用于生产环境,而不会出现像 KEYS 或者 SMEMBERS 命令带来的可能会阻塞服务器的问题。

SCAN命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次这个调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程

当SCAN命令的游标参数(即cursor)被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。

简单的迭代演示:

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命令的返回值 是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则又是一个数组, 这个数组中包含了所有被迭代的元素。

注意:返回的游标不一定是递增的,可能后一次返回的游标比前一次的小。
在第二次调用 SCAN 命令时, 命令返回了游标 0 , 这表示迭代已经结束, 整个数据集已经被完整遍历过了。

full iteration :以 0 作为游标开始一次新的迭代, 一直调用 SCAN 命令, 直到命令返回游标 0 , 我们称这个过程为一次完整遍历。

SCAN增量式迭代命令并不保证每次执行都返回某个给定数量的元素,甚至可能会返回零个元素, 但只要命令返回的游标不是 0 , 应用程序就不应该将迭代视作结束。

不过命令返回的元素数量总是符合一定规则的, 对于一个大数据集来说, 增量式迭代命令每次最多可能会返回数十个元素;而对于一个足够小的数据集来说,可能会一次迭代返回所有的key

COUNT选项

对于增量式迭代命令不保证每次迭代所返回的元素数量,我们可以使用COUNT选项, 对命令的行为进行一定程度上的调整。COUNT 选项的作用就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素。使用COUNT 选项对于对增量式迭代命令相当于一种提示, 大多数情况下这种提示都比较有效的控制了返回值的数量。

注意:COUNT选项并不能严格控制返回的key数量,只能说是一个大致的约束。并非每次迭代都要使用相同的 COUNT 值,用户可以在每次迭代中按自己的需要随意改变 COUNT 值, 只要记得将上次迭代返回的游标用到下次迭代里面就可以了。

MATCH 选项

类似于KEYS 命令,增量式迭代命令通过给定 MATCH 参数的方式实现了通过提供一个 glob 风格的模式参数, 让命令只返回和给定模式相匹配的元素。

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 , 强制命令为本次迭代扫描更多元素, 从而使得命令返回的元素也变多了。

基于SCAN的这种安全性,建议大家在生产环境都使用SCAN命令来代替KEYS,不过注意,该命令是在2.8.0版本之后加入的,如果你的Redis低于这个版本,则需要升级Redis。

下面用PHP代码演示SCAN命令的使用:

<?php
 
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
/* 设置遍历的特性为不重复查找,该情况下扩展只会scan一次,所以可能会返回空集合 */
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
$it = NULL;
$pattern = '*';
$count = 50;  // 每次遍历50条,注意是遍历50条,遍历出来的50条key还要去匹配你的模式,所以并不等于就能够取出50条key
do
{
    $keysArr = $redis->scan($it, $pattern, $count);
    if ($keysArr)
    {
        foreach ($keysArr as $key)
        {
            echo $key . "\n";
        }
    }
} while ($it > 0);   //每次调用 Scan会自动改变 $it 值,当$it = 0时 这次遍历结束 退出循环
echo '---------------------------------------------------------------------------------' . "\n";
/* 设置扩展在一次scan没有查找出记录时 进行重复的scan 直到查询出结果或者遍历结束为止 */
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
//这种用法下我们只需要简单判断返回结果是否为空即可, 如果为空说明遍历结束
while ($keysArr = $redis->scan($it, $pattern, $count))
    foreach ($keysArr as $key)
        echo $key . "\n";
}

执行结果:

[root@localhost php]# /usr/local/php/bin/php scan.php
bm
bm2
h1
name
bit
bm1
places
cities
hhl
---------------------------------------------------------------------------------
bm
bm2
h1
name
bit
bm1
places
cities
hhl

注意:如果php执行报错 请升级到较新版本的Redis扩展

更多请参考:

http://www.redis.cn/commands/keys.html

http://www.redis.cn/commands/scan.html

https://github.com/phpredis/phpredis#scan

到此这篇关于Redis遍历所有key的两个命令(KEYS 和 SCAN)的文章就介绍到这了,更多相关Redis遍历所有key内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Redis 相关文章推荐
基于Redis过期事件实现订单超时取消
May 08 Redis
为Java项目添加Redis缓存的方法
May 18 Redis
浅谈redis缓存在项目中的使用
May 20 Redis
详解Redis瘦身指南
May 26 Redis
Redis读写分离搭建的完整步骤
Sep 14 Redis
详解Redis在SpringBoot工程中的综合应用
Oct 16 Redis
浅谈Redis跟MySQL的双写问题解决方案
Feb 24 Redis
Redis集群节点通信过程/原理流程分析
Mar 18 Redis
Redis安装使用RedisJSON模块的方法
Mar 23 Redis
Redis 报错 error:NOAUTH Authentication required
May 15 Redis
Redis keys命令的具体使用
Jun 05 Redis
redis protocol通信协议及使用详解
Jul 15 Redis
解决redis sentinel 频繁主备切换的问题
redis连接被拒绝的解决方案
Redis如何一键部署脚本
浅谈redis五大数据结构和使用场景
redis配置文件中常用配置详解
Apr 14 #Redis
Redis安装启动及常见数据类型
redis限流的实际应用
Apr 24 #Redis
You might like
php中用socket模拟http中post或者get提交数据的示例代码
2013/08/08 PHP
php使用百度天气接口示例
2014/04/22 PHP
给ECShop添加最新评论
2015/01/07 PHP
php连接MSsql server的五种方法总结
2018/03/04 PHP
Laravel 实现关系模型取出需要的字段
2019/10/10 PHP
运用Windows XP附带的Msicuu.exe、Msizap.exe来彻底卸载顽固程序
2007/04/21 Javascript
为JavaScript提供睡眠功能(sleep) 自编译JS引擎
2010/08/16 Javascript
热点新闻滚动特效的js代码
2013/08/17 Javascript
js兼容的placeholder属性详解
2013/08/18 Javascript
js 动态加载事件的几种方法总结
2013/12/25 Javascript
javascript获取dom的下一个节点方法
2014/09/05 Javascript
jQuery选择器总结之常用元素查找方法
2016/08/04 Javascript
JavaScript trim 实现去除字符串首尾指定字符的简单方法
2016/12/27 Javascript
JS简单实现数组去重的方法示例
2017/03/27 Javascript
jquery实现一个全局计时器(商城可用)
2017/06/30 jQuery
详解使用Vue Router导航钩子与Vuex来实现后退状态保存
2017/09/11 Javascript
JavaScript常用截取字符串的三种方式用法区别实例解析
2018/05/15 Javascript
Node.js Buffer用法解读
2018/05/18 Javascript
angular 数据绑定之[]和{{}}的区别
2018/09/25 Javascript
echarts实现折线图的拖拽效果
2019/12/19 Javascript
理解Proxy及使用Proxy实现vue数据双向绑定操作
2020/07/18 Javascript
[01:48]完美圣典齐天大圣至宝宣传片
2016/12/17 DOTA
python使用Image处理图片常用技巧分析
2015/06/01 Python
MySQL中表的复制以及大型数据表的备份教程
2015/11/25 Python
学习Python selenium自动化网页抓取器
2018/01/20 Python
python实现机器人行走效果
2018/01/29 Python
Python实现全排列的打印
2018/08/18 Python
pycham查看程序执行的时间方法
2018/11/29 Python
ParcelABC西班牙:包裹运送和快递服务
2019/12/24 全球购物
中学生校园广播稿
2014/01/16 职场文书
航海技术专业毕业生推荐信
2014/07/09 职场文书
给医院的感谢信
2015/01/21 职场文书
实习介绍信模板
2015/01/30 职场文书
员工手册编写范本
2015/05/14 职场文书
安全教育主题班会教案
2015/08/12 职场文书
2016继续教育研修日志
2015/11/13 职场文书