Redis查找指令——SCAN

目錄

 

1、keys鍵命令缺點

2、引入scan命令

3、scan使用

4、更多scan指令

5、允許中途停止迭代

6、迭代什麼時候終結

7、時間複雜度


1、keys鍵命令缺點

    Redis 提供了一個簡單暴力的指令 keys 用來列出所有滿足特定正則字符串規則的key。keys指令使用非常簡單,提供一個簡單的正則字符串即可,但是有很明顯的兩個缺點:
    1)沒有 offset、limit 參數,一次性吐出所有滿足條件的 key,如果實例中有幾百萬個key滿足條件,那就可能導致滿屏的字符串刷的沒有盡頭;
    2)keys算法是遍歷算法,複雜度是 O(n),如果實例中有千萬級以上的 key,這個指令就會導致Redis服務卡頓,甚至造成阻塞,因爲Redis是單線程程序,順序執行所有指令,其它指令必須等到當前的 keys 指令執行完了纔可以繼續。
    所以,生產環境一般是屏蔽keys命令的。

2、引入scan命令

    SCAN命令是一個基於遊標的迭代器。這裏的意思是:命令每次被調用都需要使用上一次該調用返回的遊標作爲本次調用的遊標參數,以此來延續之前的迭代過程。當設置SCAN命令的遊標參數爲0時,服務器將開始一次新的迭代;而當redis服務器向用戶返回值爲0的遊標時,表示迭代已結束,這是唯一迭代結束的判定方式,而不能通過返回結果集是否爲空判斷迭代結束
scan相比keys具備有以下優點:
    1)複雜度雖然也是 O(n),但是它是通過遊標分步進行的,不會阻塞線程;
    2)提供 limit 參數,可以控制每次返回結果的最大條數,limit 只是對增量式迭代命令的一種提示(hint),返回的結果可多可少;
    3)服務器不需要爲遊標保存狀態,遊標的唯一狀態就是 scan 返回給客戶端的遊標整數;
------------
scan也有缺點:
    1)返回的結果可能會有重複,需要客戶端去重複,這點非常重要;
    2)遍歷的過程中如果有數據修改,改動後的數據能不能遍歷到是不確定的。即如果一個元素是在迭代過程中被添加到數據集的, 又或者是在迭代過程中從數據集中被刪除的, 那麼這個元素可能會被返回, 也可能不會, 這是未定義未知的。
    3)單次返回的結果是空的並不意味着遍歷結束,而要看返回的遊標值是否爲零
 

3、scan使用

redis-cli scan [cursor] match [pattern] count [limit]
1、提供3個參數:
  1. cursor整數值  用戶將遊標設置爲0,表示開始新一次的迭代
  2. pattern正則表達式
  3. count limit 
    1. 默認10 
    2. 注意這裏的limit並不是限定返回結果的數量,而是限定服務器單次遍歷的字典槽位數)
    3. 注意並非每次迭代都要使用相同的 COUNT 值。用戶可以在每次迭代中按自己的需要隨意改變 COUNT 值, 只要記得將上次迭代返回的遊標用到下次迭代裏面就可以了
    4. 使用了錯誤的遊標。使用間斷的(broken)、負數、超出範圍或者其他非正常的遊標來執行增量式迭代並不會造成服務器崩潰, 但可能會讓命令產生未定義的行爲。
    5. 遊標的合法值只有2個:
      1. 在開始一個新的迭代時, 遊標必須爲0;
      2. 使用前一次迭代命令返回的迭代遊標值。
2、返回值:
  1. 下一次迭代遊標
  2. 本次迭代結果集(有可能爲空)
3、scan過程:
    第一次遍歷時,cursor 值爲0,後續將返回結果中第一個整數值作爲下一次遍歷的cursor。一直遍歷到redis返回的遊標值爲0時結束。
    以0 作爲遊標開始一次新的迭代, 一直調用 SCAN 命令, 直到命令返回遊標 0 , 我們稱這個過程爲一次完整遍歷。
 

4、scan指令實例:

$ redis-cli scan 0 match key99* count 1000
1) "13912"
2)  1) "key997"
    2) "key9906"
    3) "key9957"
    4) "key9902"
    5) "key9971"
    6) "key9935"
    7) "key9958"
    8) "key9928"
    9) "key9931"
   10) "key9961"
   11) "key9948"
   12) "key9965"
   13) "key9937"
   
$ redis-cli scan 13912 match key99* count 1000
1) "5292"
2)  1) "key996"
    2) "key9960"
    3) "key9973"
    4) "key9978"
    5) "key9927"
    6) "key995"
    7) "key9992"
    
   從上面的過程可以看到雖然設置的limit是1000,但是返回的結果只有 10 個左右。這是因爲因爲這個 limit 不是限定返回結果的數量,而是限定服務器單次遍歷的字典槽位數量(約等於)。所以如果將limit 設置爲 10,你會發現返回結果是空的,但是遊標值不爲零,意味着遍歷還沒結束。

如果將limit設置爲10,例如下:
$ redis-cli scan 0 match key99* count 10
1) "15360"
2) (empty list or set)

$ redis-cli scan 15360 match key99* count 10
1) "2304"
2) (empty list or set)

4、更多scan指令

scan 指令是一系列指令,除了可以遍歷所有的 key 之外,還可以對指定的容器集合進行遍歷。’

  • SCAN 命令用於迭代當前數據庫中的數據庫鍵,
  • zscan 遍歷 zset 集合元素,
  • hscan 遍歷 hash 字典的鍵值對,
  • sscan 遍歷 set 集合的元素。

    注意,SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一個參數總是一個數據庫鍵。而 SCAN 命令則不需要在第一個參數提供任何數據庫鍵 —— 因爲它迭代的是當前數據庫中的所有數據庫鍵。

5、允許中途停止迭代

    因爲迭代的所有狀態都保存在遊標裏面, 而服務器無須爲迭代保存任何狀態, 所以客戶端可以在中途停止一個迭代, 而無須對服務器進行任何通知。即使有任意數量的迭代在中途停止, 也不會產生任何問題。

6、迭代什麼時候終結

    當redis服務器向用戶返回值爲0的遊標時,表示迭代已結束,這是唯一迭代結束的判定方式,而不能通過返回結果集是否爲空判斷迭代結束。

    同時增量式迭代命令所使用的算法只保證在數據集的大小有界(bounded)的情況下, 迭代纔會停止, 換句話說, 如果被迭代數據集的大小不斷地增長的話, 增量式迭代命令可能永遠也無法完成一次完整迭代。

7、時間複雜度

    每次執行的複雜度爲 O(1),對數據集進行一次完整迭代的複雜度爲 O(N),其中 N 爲數據集中的元素數量。

 

8、參考資料

http://jinguoxing.github.io/redis/2018/09/04/redis-scan/

http://doc.redisfans.com/key/scan.html

 

發佈了48 篇原創文章 · 獲贊 35 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章