💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 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简介 时间复杂度: 增量式迭代命令每次执行的复杂度为 O(1) , 对数据集进行一次完整迭代的复杂度为 O(N) , 其中 N 为数据集中的元素数量。 SCAN 命令及其相关的 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都用于增量地迭代(incrementally iterate)一集元素(a collection of elements): * SCAN 命令用于迭代当前数据库中的数据库键。 * SSCAN 命令用于迭代集合键中的元素。 * HSCAN 命令用于迭代哈希键中的键值对。 * ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值)。 以上列出的四个命令都支持增量式迭代, 它们每次执行都只会返回少量元素, 所以这些命令可以用于生产环境, 而不会出现像 KEYS命令、 SMEMBERS 命令带来的问题 —— 当 KEYS 命令被用于处理一个大的数据库时, 又或者 SMEMBERS 命令被用于处理一个大的集合键时, 它们可能会阻塞服务器达数秒之久。 不过, 增量式迭代命令也不是没有缺点的: 举个例子, 使用 SMEMBERS 命令可以返回集合键当前包含的所有元素, 但是对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证 (offer limited guarantees about the returned elements)。 因为 SCAN 、 SSCAN 、 HSCAN 和 ZSCAN 四个命令的工作方式都非常相似, 所以这个文档会一并介绍这四个命令, 但是要记住: SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一个参数总是一个数据库键。 而 SCAN 命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键 SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。 # 演示 当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); $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 ~~~