1. 程式人生 > 資料庫 >Redis查詢指令——SCAN

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