Redis以高性能著称,但性能再好,在面对海量数据时,若不正确的使用,也终将会有性能瓶颈,甚至形成服务宕机。php
在实际项目中你是否会有如下疑问?java
以上问题亦是Redis面试的高频问题。git
《玩转Redis》系列文章主要讲述Redis的基础及中高级应用,文章基于Redis5.0.4+,欢迎前往CSDN、订阅号、开源中国、掘金等平台搜索【zxiaofan】查看系列文章。github
Q1:为何Redis中的数据量很大时,某些数据操做会致使Redis卡顿,甚至宕机?面试
A1:Redis是单线程服务,全部指令都是顺序执行,当某一指令耗时很长时,就会阻塞后续的指令执行。当被积压的指令愈来愈多时,Redis服务占用CPU将不断升高,最终致使Redis实例崩溃甚至服务器宕机。redis
Q2:利用万能的keys命令查询任何想查的数据?数据库
A2:本身电脑几万条数据玩玩就行了,线上使用keys命令,Excuse me?你想卷铺盖走人了吧。
++“某公司php工程师执行redis keys * 致使数据库宕机! 技术部发生2起本年度PO级特大事故,形成公司资金损失400万。”++ 这条新闻记忆犹新,警钟长鸣!数组
Q3:Redis中海量数据的正确操做方式服务器
A3:利用SCAN系列命令(SCAN、SSCAN、HSCAN、ZSCAN)完成数据迭代。微信
Redis的【SCAN系列命令】你了解多少呢?
SCAN系列命令,并不单纯指代SCAN命令,还包含SSCAN、HSCAN、ZSCAN,每种命令操做对象是有区别的,但用法及功能基本相同。
命令 | 功能 | 参数 | 返回值 |
---|---|---|---|
SCAN | 基于游标迭代DB | cursor [MATCH pattern] [COUNT count] | 返回数组,第一个值是下一次迭代的游标(无符号64bit),第2个值是元素列表(key列表) |
SSCAN | 基于游标迭代Sets | key cursor [MATCH pattern] [COUNT count] | 返回数组,第一个值是下一次迭代的游标(无符号64bit),第2个值是元素列表 |
HSCAN | 基于游标迭代Hashes | key cursor [MATCH pattern] [COUNT count] | 返回数组,第2个值是field-value列表 |
ZSCAN | 基于游标迭代ZSets | key cursor [MATCH pattern] [COUNT count] | 返回数组,第2个值是member-score列表 |
详见《5.二、部分问题解答》
// SSCAN示例 @zxiaofan
127.0.0.1:6378> SADD sscantest sscantest:1 1 sscantest:2 2 sscantest:3 3 sscantest:4 4 sscantest:1a 1a sscantest:2a 2a sscantest:1ab 1ab sscantest:a1 a1 sscantest:aa1 aa1 (integer) 0 // MATCH ?:无匹配数据 127.0.0.1:6378> SSCAN sscantest 0 MATCH ? COUNT 1 1) "24" 2) (empty list or set) 127.0.0.1:6378> SSCAN sscantest 24 MATCH ? COUNT 1 1) "20" 2) (empty list or set) 127.0.0.1:6378> SSCAN sscantest 0 MATCH * COUNT 1 1) "24" 2) 1) "sscantest:3" 2) "sscantest:2a" 127.0.0.1:6378> SSCAN sscantest 24 MATCH * COUNT 1 1) "20" 2) 1) "a1" 复制代码
// HSCAN示例 @zxiaofan
127.0.0.1:6378> HMSET hscantest hscantest:1 1 hscantest:2 2 hscantest:3 3 hscantest:4 4 hscantest:1a 1a hscantest:2a 2a hscantest:1ab 1ab hscantest:a1 a1 hscantest:aa1 aa1
OK
127.0.0.1:6378> HSCAN hscantest 0 MATCH hscantest*a COUNT 20
1) "0"
2) 1) "hscantest:1a"
2) "1a"
3) "hscantest:2a"
4) "2a"
127.0.0.1:6378> HSCAN hscantest 0 MATCH hscantest*a COUNT 2
1) "0"
2) 1) "hscantest:1a"
2) "1a"
3) "hscantest:2a"
4) "2a"
127.0.0.1:6378>
复制代码
从HSCAN示例能够看出,即便count参数为2,也返回了全部匹配的结果。这就是先前提到的,数据量较小时,直接返回全部数据。
// ZSCAN示例 @zxiaofan
// 【移除】并弹出count个分数最大的元素,count默认为1
127.0.0.1:6378> ZPOPMAX zscantest 20
1) "sscantest:1ab"
2) "6"
3) "sscantest:2a"
4) "5"
5) "sscantest:1a"
6) "4"
7) "sscantest:3"
8) "3"
9) "zscantest:1"
10) "2"
11) "sscantest:2"
12) "2"
13) "test1"
14) "1"
15) "sscantest:1"
16) "1"
127.0.0.1:6378> ZPOPMAX zscantest 20
(empty list or set)
127.0.0.1:6378> ZADD zscantest 1 zscantest:1 2 zscantest:2 3 zscantest:3 4 zscantest:1a 5 zscantest:2a 6 zscantest:1ab 7 zscantest:a1 8 zscantest:aa1
(integer) 8
// NX:不存在才添加;CH:返回被改变(含新增)的元素个数
127.0.0.1:6378> ZADD zscantest NX CH 1 test1 2 zscantest:1
(integer) 1
127.0.0.1:6378> ZSCAN zscantest 0 MATCH *a COUNT 5
1) "0"
2) 1) "zscantest:1a"
2) "4"
3) "zscantest:2a"
4) "5"
127.0.0.1:6378>
复制代码
// SCAN返回数据为空就是迭代结束了吗? @zxiaofan
127.0.0.1:6378> keys k?
1) "k1"
2) "k2"
127.0.0.1:6378> SCAN 0 MATCH k?
1) "88"
2) (empty list or set)
127.0.0.1:6378> SCAN 88 MATCH k?
1) "34"
2) 1) "k1"
127.0.0.1:6378> SCAN 34 MATCH k?
1) "122"
2) (empty list or set)
127.0.0.1:6378> SCAN 122 MATCH k?
1) "14"
2) (empty list or set)
127.0.0.1:6378> SCAN 14 MATCH k?
1) "33"
2) (empty list or set)
127.0.0.1:6378> SCAN 33 MATCH k?
1) "53"
2) (empty list or set)
127.0.0.1:6378> SCAN 53 MATCH k?
1) "93"
2) (empty list or set)
127.0.0.1:6378> SCAN 93 MATCH k?
1) "107"
2) 1) "k2"
127.0.0.1:6378> SCAN 107 MATCH k?
1) "79"
2) (empty list or set)
127.0.0.1:6378> SCAN 79 MATCH k?
1) "0"
2) (empty list or set)
127.0.0.1:6378>
复制代码
看上述示例,匹配“k?”的数据实际有2条“k1”、“k2”,在整个迭代过程当中,屡次返回数据为空,可是迭代不曾结束(由于“k1”、“k2”没有所有迭代返回)。
因此,只有当游标返回为0时,才能说明迭代结束了。
// 若是首次迭代cursor参数不是0,能实现完整迭代吗? @zxiaofan
127.0.0.1:6378> keys k?
1) "k1"
2) "k2"
127.0.0.1:6378> SCAN 66 MATCH k?
1) "122"
2) (empty list or set)
127.0.0.1:6378> SCAN 122 MATCH k?
1) "14"
2) (empty list or set)
127.0.0.1:6378> SCAN 14 MATCH k?
1) "33"
2) (empty list or set)
127.0.0.1:6378> SCAN 33 MATCH k?
1) "53"
2) (empty list or set)
127.0.0.1:6378> SCAN 53 MATCH k?
1) "93"
2) (empty list or set)
127.0.0.1:6378> SCAN 93 MATCH k?
1) "107"
2) 1) "k2"
127.0.0.1:6378> SCAN 107 MATCH k?
1) "79"
2) (empty list or set)
127.0.0.1:6378> SCAN 79 MATCH k?
1) "0"
2) (empty list or set)
127.0.0.1:6378>
复制代码
看上述示例,匹配“k?”的数据实际有2条“k1”、“k2”,当第一次SCAN使用cursor为66,咱们能够发现通过屡次迭代,游标返回为0时,“k1”一直不曾被迭代返回。
因此,若是首次迭代cursor参数不是0,不能实现完整迭代。
完整迭代必须是游标从0开始,游标到0结束。
本文针对Redis的SCAN系列命令作了详细的对比分析以及实际使用示例,并整理了面试中的高频问题。建议阅读本文的同窗实际动手练习下,效果更好。欢迎关注@zxiaofan《玩转Redis》系列文章共同成长。
第5节提到的面试问题,如今能答上几个了呢?
祝君好运!
Life is all about choices!
未来的你必定会感激如今拼命的本身!
【CSDN】【GitHub】【OSCHINA】【掘金】【微信公众号】