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通过6379端口无法连接服务器(redis-server.exe闪退)
May 08 Redis
基于Redis位图实现用户签到功能
May 08 Redis
redis三种高可用方式部署的实现
May 11 Redis
Windows下redis下载、redis安装及使用教程
Jun 02 Redis
redis cluster支持pipeline的实现思路
Jun 23 Redis
解析redis hash应用场景和常用命令
Aug 04 Redis
解决redis批量删除key值的问题
Mar 23 Redis
解决 Redis 秒杀超卖场景的高并发
Apr 12 Redis
windows安装 redis 6.2.6最新步骤详解
Apr 26 Redis
Redis高并发缓存架构性能优化
May 15 Redis
浅谈Redis缓冲区机制
Jun 05 Redis
Redis配置外网可访问(redis远程连接不上)的方法
Dec 24 Redis
解决redis sentinel 频繁主备切换的问题
redis连接被拒绝的解决方案
Redis如何一键部署脚本
浅谈redis五大数据结构和使用场景
redis配置文件中常用配置详解
Apr 14 #Redis
Redis安装启动及常见数据类型
redis限流的实际应用
Apr 24 #Redis
You might like
供参考的 php 学习提高路线分享
2011/10/23 PHP
分享一个PHP数据流应用的简单例子
2012/06/01 PHP
yiic命令时提示“php.exe”不是内部或外部命令的解决方法
2014/12/18 PHP
PHP判断一个字符串是否是回文字符串的方法
2015/03/23 PHP
php中10个不同等级压缩优化图片操作示例
2016/11/14 PHP
零基础php编程好学吗
2019/10/11 PHP
InnerHtml和InnerText的区别分析
2009/03/13 Javascript
jQuery实现Tab菜单滚动切换的方法
2015/09/21 Javascript
JavaScript中各种引用类型的常用操作方法小结
2016/05/05 Javascript
输入法的回车与消息发送快捷键回车的冲突解决方法
2016/08/09 Javascript
原生js的RSA和AES加密解密算法
2016/10/08 Javascript
jQuery表格(Table)基本操作实例分析
2017/03/10 Javascript
bootstrap弹出层的多种触发方式
2017/05/10 Javascript
EasyUI Datebox 日期验证之开始日期小于结束时间
2017/05/19 Javascript
vue实现一个移动端屏蔽滑动的遮罩层实例
2017/06/08 Javascript
ionic 3.0+ 项目搭建运行环境的教程
2017/08/09 Javascript
JQuery模拟实现网页中自定义鼠标右键菜单功能
2018/11/14 jQuery
nodejs微信开发之授权登录+获取用户信息
2019/03/17 NodeJs
详解vue的数据劫持以及操作数组的坑
2019/04/18 Javascript
vue 解决路由只变化参数页面组件不更新问题
2019/11/05 Javascript
Vue2.0 $set()的正确使用详解
2020/07/28 Javascript
在antd4.0中Form使用initialValue操作
2020/11/02 Javascript
[05:07]DOTA2英雄梦之声_第14期_暗影恶魔
2014/06/20 DOTA
python matplotlib中的subplot函数使用详解
2020/01/19 Python
python如何实现读取并显示图片(不需要图形界面)
2020/07/08 Python
python动态规划算法实例详解
2020/11/22 Python
使用CSS3的appearance属性改变元素的外观的方法
2015/12/12 HTML / CSS
canvas实现扭蛋机动画效果的示例代码
2018/10/17 HTML / CSS
德国最大的网上足球商店:11teamsports
2019/09/11 全球购物
PHP如何删除一个Cookie值
2012/11/15 面试题
企业标语大全
2014/07/01 职场文书
战略性融资合作协议书范本
2014/10/17 职场文书
2014年领班工作总结
2014/11/25 职场文书
教师年度考核个人总结
2015/02/12 职场文书
一起来学习Python的元组和列表
2022/03/13 Python
i7 6700处理器相当于i5几代
2022/04/19 数码科技