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】【掘金】【微信公衆號】