[圖文講解]redis命令keys和scan的區別


1. Keys 命令

語法

redis KEYS 命令基本語法如下:

redis 127.0.0.1:6379> KEYS PATTERN

KEYS命令使用很簡單,查找以 com 為開頭的 key:

2 .Scan 命令用於迭代數據庫中的數據庫鍵。

SCAN 命令是一個基於游標的迭代器,每次被調用之后, 都會向用戶返回一個新的游標, 用戶在下次迭代時需要使用這個新游標作為 SCAN 命令的游標參數, 以此來延續之前的迭代過程。

SCAN 返回一個包含兩個元素的數組, 第一個元素是用於進行下一次迭代的新游標, 而第二個元素則是一個數組, 這個數組中包含了所有被迭代的元素。如果新游標返回 0 表示迭代已結束。

相關命令:

  • SSCAN 命令用於迭代集合鍵中的元素。
  • HSCAN 命令用於迭代哈希鍵中的鍵值對。
  • ZSCAN 命令用於迭代有序集合中的元素(包括元素成員和元素分值)。

語法

redis Scan 命令基本語法如下:

SCAN cursor [MATCH pattern] [COUNT count]
cursor - 游標。
pattern - 匹配的模式。
count - 指定從數據集里返回多少元素,默認值為 10 。

3.區別:

 KEYS 的速度非常快,例如,Redis在一個有1百萬個key的數據庫里面執行一次查詢需要的時間是40毫秒 。但在一個大的數據庫中使用它仍然可能造成性能問題. 

假如Redis里面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如果將它們全部找出來?
答:使用keys指令可以掃出指定模式的key列表。

對方接着追問:如果這個redis正在給線上的業務提供服務,那使用keys指令會有什么問題?

這個時候你要回答redis關鍵的一個特性:redis的單線程的。keys指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重復概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。

keys命令的原理就是掃描整個redis里面所有的db的key數據,然后根據我們的通配的字符串進行模糊查找出來。更為致命的是,這個命令會阻塞redis多路復用的io主線程,如果這個線程阻塞,在此執行之間其他的發送向redis服務端的命令,都會阻塞,從而引發一系列級聯反應,導致瞬間響應卡頓,從而引發超時等問題,所以應該在生產環境禁止用使用keys和類似的命令smembers,這種時間復雜度為O(N),且會阻塞主線程的命令,是非常危險的

取而代之的,如果需要查找然后刪除key的需求,那么在生產環境我們應該使用scan命令,代替keys命令,同樣是O(N)復雜度的scan命令,支持通配查找,scan命令或者其他的scan如SSCAN ,HSCAN,ZSCAN命令,可以不用阻塞主線程,並支持游標按批次迭代返回數據,所以是比較理想的選擇。keys相比scan命令優點是,keys是一次返回,而scan是需要迭代多次返回。

但scan命令的也有缺點,返回的數據有可能重復,需要我們在業務層按需要去重,scan命令的游標從0開始,也從0結束,每次返回的數據,都會返回下一次游標應該傳的值,我們根據這個值,再去進行下一次的訪問,如果返回的數據為空,並不代表沒有數據了,只有游標返回的值是0的情況下代表結束

但由於KEYS命令一次性返回所有匹配的key,所以,當redis中的key非常多時,對於內存的消耗和redis服務器都是一個隱患,

對於Redis 2.8以上版本給我們提供了一個更好的遍歷key的命令 SCAN :

SCAN  每次執行都只會返回少量元素,所以可以用於生產環境,而不會出現像 KEYS 或者 SMEMBERS 命令帶來的可能會阻塞服務器的問題。
SCAN命令是一個基於游標的迭代器。這意味着命令每次被調用都需要使用上一次這個調用返回的游標作為該次調用的游標參數,以此來延續之前的迭代過程; 當SCAN命令的游標參數(即cursor)被設置為 0 時, 服務器將開始一次新的迭代, 而當服務器向用戶返回值為 0 的游標時, 表示迭代已結束。

簡單的迭代演示:

在上面這個例子中, 第一次迭代使用 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 選項對元素的模式匹配工作是在命令從數據集中取出元素后和向客戶端返回元素前的這段時間內進行的, 所以如果被迭代的數據集中只有少量元素和模式相匹配, 那么迭代命令或許會在多次執行中都不返回任何元素。

以下是這種情況的一個例子:

可以看出,以上的大部分迭代都不返回任何元素。在最后一次迭代, 我們通過將 COUNT 選項的參數設置為 100 , 強制命令為本次迭代掃描更多元素, 從而使得命令返回的元素也變多了。

基於SCAN的這種安全性,建議大家在生產環境都使用SCAN命令來代替KEYS,不過注意,該命令是在2.8.0版本之后加入的,如果你的Redis低於這個版本,則需要升級Redis。

下面用PHP代碼演示SCAN命令的使用:

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

/* 設置遍歷的特性為不重復查找,該情況下擴展只會scan一次,所以可能會返回空集合 */
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
$it = NULL;
$pattern = 'com*';
$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 = 'com*';
$count = 50;  // 每次遍歷50條,注意是遍歷50條,遍歷出來的50條key還要去匹配你的模式,所以並不等於就能夠取出50條key
//這種用法下我們只需要簡單判斷返回結果是否為空即可, 如果為空說明遍歷結束
while ($keysArr = $redis->scan($it, $pattern, $count)) {
    foreach ($keysArr as $key) {
        echo $key . "\n";
    }
}

參考: Redis遍歷所有key的兩個命令 -- KEYS 和 SCAN

redis命令keys和scan的區別

MySQL里有2000w數據,redis中只存20w的數據,如何保證redis中的數據都是熱點數據?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM