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

KEYS pattern

 

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

KEYS命令使用很简单。
 
  1.  
    redis> MSET one 1 two 2 three 3 four 4
  2.  
    OK
  3.  
    redis> KEYS *o*
  4.  
    1) "four"
  5.  
    2) "one"
  6.  
    3) "two"
  7.  
    redis> KEYS t??
  8.  
    1) "two"
  9.  
    redis> KEYS *
  10.  
    1) "four"
  11.  
    2) "three"
  12.  
    3) "one"
  13.  
    4) "two"
  14.  
    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 的游标时, 表示迭代已结束。

简单的迭代演示:

  1.  
    redis 127.0.0.1:6379> scan 0
  2.  
    1) "17"
  3.  
    2) 1) "key:12"
  4.  
    2) "key:8"
  5.  
    3) "key:4"
  6.  
    4) "key:14"
  7.  
    5) "key:16"
  8.  
    6) "key:17"
  9.  
    7) "key:15"
  10.  
    8) "key:10"
  11.  
    9) "key:3"
  12.  
    10) "key:7"
  13.  
    11) "key:1"
  14.  
    redis 127.0.0.1:6379> scan 17
  15.  
    1) "0"
  16.  
    2) 1) "key:5"
  17.  
    2) "key:18"
  18.  
    3) "key:0"
  19.  
    4) "key:2"
  20.  
    5) "key:19"
  21.  
    6) "key:13"
  22.  
    7) "key:6"
  23.  
    8) "key:9"
  24.  
    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 选项对元素的模式匹配工作是在命令从数据集中取出元素后和向客户端返回元素前的这段时间内进行的, 所以如果被迭代的数据集中只有少量元素和模式相匹配, 那么迭代命令或许会在多次执行中都不返回任何元素。

以下是这种情况的一个例子:

  1.  
    redis 127.0.0.1:6379> scan 0 MATCH *11*
  2.  
    1) "288"
  3.  
    2) 1) "key:911"
  4.  
    redis 127.0.0.1:6379> scan 288 MATCH *11*
  5.  
    1) "224"
  6.  
    2) (empty list or set)
  7.  
    redis 127.0.0.1:6379> scan 224 MATCH *11*
  8.  
    1) "80"
  9.  
    2) (empty list or set)
  10.  
    redis 127.0.0.1:6379> scan 80 MATCH *11*
  11.  
    1) "176"
  12.  
    2) (empty list or set)
  13.  
    redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
  14.  
    1) "0"
  15.  
    2) 1) "key:611"
  16.  
    2) "key:711"
  17.  
    3) "key:118"
  18.  
    4) "key:117"
  19.  
    5) "key:311"
  20.  
    6) "key:112"
  21.  
    7) "key:111"
  22.  
    8) "key:110"
  23.  
    9) "key:113"
  24.  
    10) "key:211"
  25.  
    11) "key:411"
  26.  
    12) "key:115"
  27.  
    13) "key:116"
  28.  
    14) "key:114"
  29.  
    15) "key:119"
  30.  
    16) "key:811"
  31.  
    17) "key:511"
  32.  
    18) "key:11"
  33.  
    redis 127.0.0.1:6379>

可以看出,以上的大部分迭代都不返回任何元素。在最后一次迭代, 我们通过将 COUNT 选项的参数设置为 1000 , 强制命令为本次迭代扫描更多元素, 从而使得命令返回的元素也变多了。

 
基于SCAN的这种安全性,建议大家在生产环境都使用SCAN命令来代替KEYS,不过注意,该命令是在2.8.0版本之后加入的,如果你的Redis低于这个版本,则需要升级Redis。
 
 
下面用PHP代码演示SCAN命令的使用:
 
  1.  
    <?php
  2.  
     
  3.  
    $redis = new Redis();
  4.  
     
  5.  
    $redis->connect('127.0.0.1', 6379);
  6.  
     
  7.  
     
  8.  
    /* 设置遍历的特性为不重复查找,该情况下扩展只会scan一次,所以可能会返回空集合 */
  9.  
    $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
  10.  
     
  11.  
    $it = NULL;
  12.  
    $pattern = '*';
  13.  
    $count = 50; // 每次遍历50条,注意是遍历50条,遍历出来的50条key还要去匹配你的模式,所以并不等于就能够取出50条key
  14.  
     
  15.  
    do
  16.  
    {
  17.  
    $keysArr = $redis->scan($it, $pattern, $count);
  18.  
     
  19.  
    if ($keysArr)
  20.  
    {
  21.  
    foreach ($keysArr as $key)
  22.  
    {
  23.  
    echo $key . "\n";
  24.  
    }
  25.  
    }
  26.  
     
  27.  
    } while ($it > 0); //每次调用 Scan会自动改变 $it 值,当$it = 0时 这次遍历结束 退出循环
  28.  
     
  29.  
     
  30.  
    echo '---------------------------------------------------------------------------------' . "\n";
  31.  
     
  32.  
     
  33.  
    /* 设置扩展在一次scan没有查找出记录时 进行重复的scan 直到查询出结果或者遍历结束为止 */
  34.  
    $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
  35.  
     
  36.  
    $it = NULL;
  37.  
    $pattern = '*';
  38.  
    $count = 50; // 每次遍历50条,注意是遍历50条,遍历出来的50条key还要去匹配你的模式,所以并不等于就能够取出50条key
  39.  
     
  40.  
    //这种用法下我们只需要简单判断返回结果是否为空即可, 如果为空说明遍历结束
  41.  
    while ($keysArr = $redis->scan($it, $pattern, $count))
  42.  
    {
  43.  
    foreach ($keysArr as $key)
  44.  
    {
  45.  
    echo $key . "\n";
  46.  
    }
  47.  
    }
 
执行结果:

  1.  
    [root@localhost php]# /usr/local/php/bin/php scan.php
  2.  
    bm
  3.  
    bm2
  4.  
    h1
  5.  
    name
  6.  
    bit
  7.  
    bm1
  8.  
    places
  9.  
    cities
  10.  
    hhl
  11.  
    ---------------------------------------------------------------------------------
  12.  
    bm
  13.  
    bm2
  14.  
    h1
  15.  
    name
  16.  
    bit
  17.  
    bm1
  18.  
    places
  19.  
    cities
  20.  
    hhl
 
注意:如果php执行报错 请升级到较新版本的Redis扩展
 
更多请参考:
 
 
 

Redis遍历所有key的两个命令 -- KEYS 和 SCAN的更多相关文章

  1. Redis系列之key操作命令与Redis中的事务详解(六)

    序言 本篇主要目的有二: 1.展示所有数据类型中key的所有操作命令,以供大家学习,查阅,更深入的挖掘redis潜力. 2.掌握redis中的事务,让你的数据完整性一致性拥有更优的保障. redis命 ...

  2. 如何解决Redis中的key过期问题

    最近我们在Redis集群中发现了一个有趣的问题.在花费大量时间进行调试和测试后,通过更改key过期,我们可以将某些集群中的Redis内存使用量减少25%. Twitter内部运行着多个缓存服务.其中一 ...

  3. Redis批量删除key的小技巧,你知道吗?

    在使用redis的过程中,经常会遇到要批量删除某种规则的key,但是redis提供了批量查询一类key的命令keys或scan,没有提供批量删除某种规则key的命令,怎么办?看完本文即可,哈哈. 本文 ...

  4. redis的hash, list, set类型相关命令

    hash相关命令: 1. hset HSET key field value 将哈希表key中的域field的值设为value.如果key不存在,一个新的哈希表被创建并进行hset操作.如果域fiel ...

  5. Redis进阶实践之十五 Redis-cli命令行工具使用详解第二部分(结束)

    一.介绍           今天继续redis-cli使用的介绍,上一篇文章写了一部分,写到第9个小节,今天就来完成第二部分.话不多说,开始我们今天的讲解.如果要想看第一篇文章,地址如下:http: ...

  6. Redis实战 - 4.Key

    Redis 键(key) Redis 键命令用于管理 redis 的键. DEL key 该命令用于在 key 存在时删除 key. 127.0.0.1:6379> set w3ckey red ...

  7. redis 中的key值过期后,触发通知事件

    1.创建springboot工程,创建监听类 maven配置 <dependencies> <dependency> <groupId>org.springfram ...

  8. Redis进阶实践之十四 Redis-cli命令行工具使用详解

    转载来源:http://www.cnblogs.com/PatrickLiu/p/8508975.html 一.介绍 redis学了有一段时间了,以前都是看视频,看教程,很少看官方的东西.现在redi ...

  9. redis push/pop(List)的17条命令

    一.Blpop 命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止.redis 127.0.0.1:6379> BLPOP LIST1 LIST2 .. ...

随机推荐

  1. Word绘制跨行表格

    如图“用户评价电影数目”,我们需要均分第一行,选中这三个个,设置了表个高度0.5cm,但是发现上面的一个比较考上,我们需要找到水平竖直居中,那么双击表格,打开表格工具,有设计和布局,切换到布局就找到了 ...

  2. 扩展gridview轻松实现冻结行和列

    在实际的项目中,由于项目的需要,数据量比较大,同时显示栏位也比较多,要做gridview里显示完整,并做到用户体验比较好,这就需要冻结表头和关键列.由于用到的地方比较多,我们可以护展一个gridvie ...

  3. Vue 单页应用:记事本

    若文章中存在内容无法加载的情况,请移步作者其他博客. 简书 CSDN 最近在看 Vue 的时候,别人给我安利了一个国外的小案例,通过 Vue 和 Vuex 来实现一个记事本. 仔细剖析下,发现“麻雀虽 ...

  4. 【开源类库学习】MBProgressHUD(提示框)

    新博客: http://www.liuchendi.com MBProgressHUD是一个开源类库,实现了各种样式的提示框, 下载地址:https://github.com/jdg/MBProgre ...

  5. javascript快速入门3--分支判断与循环

    分支结构 单一选择结构(if) 二路选择结构(if/else) 内联三元运算符 ?: 多路选择结构(switch) var condition = true; if (condition) { ale ...

  6. 我为什么学习Windows编程

    前一段时间在看TCP/IP,在图书馆里面找了不少的书,其中有几本书还是不错的.比如: <Windows网络与通信程序设计(第二版)> 王艳平著 <WinSock网络编程经络> ...

  7. 从webstorm转vscode,来一个vscode的教程和心得总结

    背景 在公司跑代码,每天卡的吐血,感觉生命都被浪费了. 再在摧残了一段时间,天天想摔电脑以后,被同事安利vscode, 那就开始搞起来 安装 这个我真的不用说了吧 插件 快捷键 shift + alt ...

  8. 倍福TwinCAT(贝福Beckhoff)应用教程11.1 TwinCAT应用小程序1 贝福IO模块介绍

    EL1002,EL1004,EL1008都是数字输入模块(2个点,4个点,8个点),输入高的范围是15V到30V,低的范围是-3V到5V         EL2002,EL2004,EL2008都是数 ...

  9. lodash forIn forOwn 遍历对象属性

    _.forIn(object, [iteratee=_.identity]) 使用 iteratee 遍历对象的自身和继承的可枚举属性. function Foo() { this.a = 1; th ...

  10. 首先给大家介绍一下数据库project师,数据库project师(Database Engineer),是从事管理和维护数据库管理系统(DBMS)

    摘要 MySQL的最初的核心思想,主要是开源.简便易用.其开发可追溯至1985年,而第一个内部发行版本号诞生,已经是1995年. 到1998年,MySQL已经能够支持10中操作系统了.当中就包含win ...