Redis 中 Keys 与 Scan 的使用
@这是小豪的第十二篇文章
上篇文章 发布后,在公众号下方的留言中看到了这么一句话
"keys 操作有点恐怖哈"
,当时就在想为啥恐怖,有点不知所以然,也没过多的去管他,直到今天在社区文章中又看到了人家的提醒"keys 操作是不行的"
.... 哈哈,需要去了解为什么啦!
keys 是什么
- keys - KEYS PATTERN
用于查找所有符合给定模式 pattern 的 key
为什么会用到 keys
$redis->keys('login:201903*')
$redis->bitop('AND', 'monthActivities'', $redis->keys('login:201903*'));
echo "连续一个月签到用户数量:" . $redis->bitCount('monthActivities');
这是上篇文章中用到 keys
的地方,当时是为了统计连续一个月签到用户数量,通过 keys('login:201903') 获取到三月所有 key
,然后加以聚合统计。
keys 使用会造成的后果
大家知道 Redis 是单线程程序,是按照顺序执行指令的,如果说我们现在正在执行 keys
命令,那么其它指令必须等到当前的 keys
指令执行完了才可以继续,再加上 keys
操作是遍历算法,复杂度是 O(n),乍一想就知道问题所在了,当实例中数据量过大的时候,Redis
服务可能会卡顿,其余指令可能会延时甚至超时报错....
再者 keys
中没有 offset、limit 参数,如果说满足查询条件的 keys
特别多,那就有点尴尬了,哈哈。
所以说官方的建议是:生产环境屏蔽掉 keys
命令。
替代方案 scan
说了那么多,这也不行那也不好的,究竟怎么办呢,Redis 为了解决这个问题,它在 2.8 版本中加入了指令:scan。
好,那我们现在就来看一下这个命令:
- scan - cursor [MATCH pattern] [COUNT count]
用于迭代当前数据库中的数据库键
相比 keys
,我们来看一下 scan
的特点:
- 复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
- 提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是对增量式迭代命令的一种提示 (hint),返回的结果可多可少;
- 同 keys 一样,它也提供模式匹配功能;
- 服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
- 返回的结果可能会有重复,需要客户端去重复,这点非常重要;
- 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
- 单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零
现在我们来实践一下:
> keys 201903*
1) "login:20190311"
2) "login:20190312"
3) "login:20190313"
> scan 0 match login:201903*
1) "0"
2) 1) "login:20190313"
2) "login:20190311"
3) "login:20190312"
> scan 0 match login:201903* count 2
1) "5"
2) 1) "login:20190313"
2) "login:20190311"
看到这里估计有点蒙圈,scan 0
是个啥意思,为啥下面的结果中第一个数据有的为 0 ,有的为 5。
其实是这样的,当我们第一次遍历查询时,cursor 值为 0,如果说数据全部查询完毕,那么返回结果的第一个数据就为 0 表示查询完毕,如果说返回的不为 0 ,那么就需要将这个数据作为下一次遍历的 cursor,也就是现在的 scan 5
,一直遍历到返回的第一个数据为 0 为止。第二个参数一目了然哈,就是控制返回数量,那究竟是不是这样呢,我们来看一下:
> scan 0
1) "0"
2) 1) "age"
2) "login:20190313"
3) "names"
4) "login:20190311"
5) "name"
6) "login:20190312"
7) "sex"
8) "ages"
> scan 0 match login:201903* count 2
1) "1"
2) 1) "login:20190313"
现在问题就来了,不是 count 2 吗。怎么查询出来的数据只有 1 条,是不是坏了?不是滴,哈哈。这是因为这个 count 不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数量(约等于),所以就明白了吧。它查出来是空的也不要担心,只要结果的第一个数据不为 0 就继续遍历循环下去。
说到这里,大家应该对,scan
有了大致的了解了吧,那我们就回归到之前的问题,统计该如何修改呢,大家来看一下基本的查询代码:
\dd($redis->scan(0, 'match', 'login:201903*', 'count', 1000));
来看一下结果:
就这样就 ok 啦,至于统计代码该如何改,我就没写出来哒,大家可以自己思考一下怎么去写,然后贴在评论区,哈哈。
结束语
至此 keys
和 scan
的讲解就结束哒,有什么不明白的或者有错误的地方,还望大家在评论区留言。
相关链接:
- 【Redis SCAN的使用】http://jinguoxing.github.io/redis/2018/09/...
- 【SCAN】http://doc.redisfans.com/key/scan.html
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: