当我们需要遍历Redis所有key或者指定模式的key时,首先想到的是KEYS命令:
KEYS pattern
KEYS
, 你最好还是用 Redis 的集合结构 来代替。 - 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>
SCAN cursor [MATCH pattern] [COUNT count]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>
-
-
- $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);
-
- $it = NULL;
- $pattern = '*';
- $count = 50; // 每次遍历50条,注意是遍历50条,遍历出来的50条key还要去匹配你的模式,所以并不等于就能够取出50条key
-
- //这种用法下我们只需要简单判断返回结果是否为空即可, 如果为空说明遍历结束
- 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