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限流的实际应用
Apr 24 Redis
Redis IP地址的绑定的实现
May 08 Redis
基于Redis过期事件实现订单超时取消
May 08 Redis
redis实现共同好友的思路详解
May 26 Redis
Redis 彻底禁用RDB持久化操作
Jul 09 Redis
浅谈Redis位图(Bitmap)及Redis二进制中的问题
Jul 15 Redis
Redis高可用集群redis-cluster详解
Mar 20 Redis
使用Redis做预定库存缓存功能
Apr 02 Redis
Redis特殊数据类型HyperLogLog基数统计算法讲解
Jun 01 Redis
Redis基本数据类型哈希Hash常用操作命令
Jun 01 Redis
使用Redis实现分布式锁的方法
Jun 16 Redis
Redis Lua脚本实现ip限流示例
Jul 15 Redis
解决redis sentinel 频繁主备切换的问题
redis连接被拒绝的解决方案
Redis如何一键部署脚本
浅谈redis五大数据结构和使用场景
redis配置文件中常用配置详解
Apr 14 #Redis
Redis安装启动及常见数据类型
redis限流的实际应用
Apr 24 #Redis
You might like
ninety plus是什么?ninety plus咖啡好吗?
2021/03/04 新手入门
php仿discuz分页效果代码
2008/10/02 PHP
关于Yii2框架跑脚本时内存泄漏问题的分析与解决
2019/12/01 PHP
jQuery之按钮组件的深入解析
2013/06/19 Javascript
Jquery倒数计时按钮setTimeout的实例代码
2013/07/04 Javascript
javascript检查浏览器是否支持flash的实现代码
2014/08/14 Javascript
js实现单击图片放大图片的方法
2015/02/17 Javascript
js带前后翻页的图片切换效果代码分享
2015/09/08 Javascript
JS实现的自定义显示加载等待图片插件(loading.gif)
2016/06/17 Javascript
Vue方法与事件处理器详解
2016/12/01 Javascript
uploader秒传图片到服务器完整代码
2017/04/22 Javascript
JavaScript学习笔记之惰性函数示例详解
2017/08/27 Javascript
iview给radio按钮组件加点击事件的实例
2017/09/30 Javascript
解决vue props 拿不到值的问题
2018/09/11 Javascript
通过vue-cli3构建一个SSR应用程序的方法
2018/09/13 Javascript
在vue中实现禁止屏幕滚动,禁止屏幕滑动
2020/07/22 Javascript
[03:05]《我与DAC》之xiao8:DAC与BG
2018/03/27 DOTA
跟老齐学Python之模块的加载
2014/10/24 Python
R vs. Python 数据分析中谁与争锋?
2017/10/18 Python
python实战之实现excel读取、统计、写入的示例讲解
2018/05/02 Python
python中的常量和变量代码详解
2018/07/25 Python
关于PyTorch源码解读之torchvision.models
2019/08/17 Python
tensorflow实现二维平面模拟三维数据教程
2020/02/11 Python
Groupon西班牙官方网站:在线优惠券和交易,节省高达70%
2021/03/13 全球购物
《宿建德江》教学反思
2014/04/23 职场文书
合作协议书格式
2014/08/19 职场文书
银行员工犯错检讨书
2014/09/16 职场文书
企业党的群众路线教育实践活动学习心得体会
2014/10/31 职场文书
2015年质量月活动总结报告
2015/03/27 职场文书
同意离婚答辩状
2015/05/22 职场文书
总结几个非常实用的Python库
2021/06/26 Python
使用redis实现延迟通知功能(Redis过期键通知)
2021/09/04 Redis
国庆节到了,利用JS实现一个生成国庆风头像的小工具 详解实现过程
2021/10/05 Javascript
python多线程方法详解
2022/01/18 Python
Linux中文件的基本属性介绍
2022/06/01 Servers
CentOS7设置ssh服务以及端口修改方式
2022/12/24 Servers