1. 程式人生 > 資料庫 >Redis Scan命令的基本使用方法

Redis Scan命令的基本使用方法

1. 概述

SCAN 命令以及比較相近的 SSCAN、HSCAN 和 ZSCAN 命令都用於增量迭代資料集元素:

  • SCAN 命令用於迭代當前資料庫中的資料庫鍵。
  • SSCAN 命令用於迭代集合(Set)中的元素。
  • HSCAN 命令用於迭代雜湊(Hash)中的欄位以及對應的值。
  • ZSCAN 命令用於迭代有序集合(Sorted Set)中的元素以及對應的得分。

由於這些命令都可以增量迭代,每次呼叫都只會返回少量元素,所以這些命令可以用於生產環境中,不用擔心像使用 KEYS、SMEMBERS 命令帶來的問題。在鍵或元素的大資料集上呼叫這些命令可能會長時間(甚至幾秒鐘)阻塞伺服器。像 SMEMBERS 這樣的阻塞命令能夠在給定的時間內提供資料集中所有的元素,但 SCAN 系列命令僅對返回的元素提供有限的保證,因為資料集在我們增量迭代時可能會發生改變。

SCAN,SSCAN,HSCAN 以及 ZSCAN 命令工作原理都非常類似,因此這篇文章會涵蓋這四個命令。區別在於 SSCAN,HSCAN 以及 ZSCAN 命令,第一個引數是儲存 Set,Hash或 Sorted Set 值的鍵的名稱。SCAN命令不需要任何鍵名引數,因為它會迭代當前資料庫中所有的鍵,因此迭代的物件是資料庫本身。

2. 基本用法

SCAN 是基於遊標的迭代器。這意味著在每次呼叫該命令時,伺服器都會返回一個更新後的新遊標,使用者需要在下一次呼叫中將這個新遊標作為 SCAN 命令的遊標引數。當 SCAN 命令的遊標引數被設定為 0 時, 伺服器將開始一次新的迭代,而當伺服器向用戶返回的新遊標為 0 時會終止迭代。以下是 SCAN 迭代的示例:

redis 127.0.0.1:6379> scan 0
1) "17"
2) 1) "key:12"
 2) "key:8"
 3) "key:4"
 4) "key:14"
 5) "key:16"
 6) "key:17"
 7) "key:15"
 8) "key:10"
 9) "key:3"
 10) "key:7"
 11) "key:1"
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
 2) "key:18"
 3) "key:0"
 4) "key:2"
 5) "key:19"
 6) "key:13"
 7) "key:6"
 8) "key:9"
 9) "key:11"

在上面的示例中,第一次呼叫使用 0 作為遊標來開始一次新的迭代。第二次呼叫時使用上一次呼叫返回的遊標,即命令回覆的第一個元素值,即17。從上面的示例可以看到,SCAN 命令返回值是兩個值的陣列:第一個值是下一次呼叫中將要使用的新遊標,第二個值是包含返回元素的陣列。

由於在第二次呼叫中返回的遊標為 0,因此伺服器向呼叫者傳送訊號,告知迭代已完成,並且遍歷完資料集。從遊標值 0 開始迭代,然後呼叫 SCAN 直到返回的遊標再次為 0,表示一個完整迭代。

3. 保證

SCAN 命令,以及其他增量迭代命令,在整個完整迭代過程中可以為使用者提供一系列的保證:

  • 在完整迭代開始直到完整迭代結束期間內的所有元素都會被遍歷返回;這意味著,如果某個給定元素在開始迭代時位於資料集內,並且在終止迭代時仍然存在,那麼 SCAN 會在某次迭代時返回給使用者。
  • 在完整迭代開始直到完整迭代結束期間內不存在的元素永遠都不會被返回;因此,如果某個元素在迭代開始之前就被刪除,並且在後續的迭代過程中從未添加回資料集中,那麼 SCAN 永遠都不會返回該元素 。

但是,由於 SCAN 只有很少的關聯狀態(僅有遊標),因此具有以下缺點:

  • 同一個元素可能會被返回多次。重複元素的問題需要我們自己的應用程式處理, 例如,可以考慮將迭代返回的元素用於冪等操作(可以重複執行多次操作)上。
  • 如果一個元素是在迭代過程中被新增到資料集的,又或者是在迭代過程中從資料集中被刪除的,那麼這個元素可能會被返回,也可能不會。

4. 每次執行返回數量

SCAN 系列的函式不能保證每次呼叫返回的元素數量會在給定範圍內。每次呼叫可能會返回 0 個元素,但只要返回的遊標不為 0,客戶端就認為迭代沒有結束(即使返回了 0 個元素也不能表示迭代的結束)。返回的元素數量會符合一定的規則:

  • 在迭代大型資料集時,SCAN 最多可能會返回幾十個元素。
  • 在迭代小的資料集並且內部為編碼資料結構時(小的 Set、Hashe 以及 Sorted Set),單次呼叫就可以返回資料集的所有元素。

但是,使用者可以使用 COUNT 引數來調整每次呼叫返回的元素的數量級。

5. COUNT引數

雖然 SCAN 不能保證每次迭代返回的元素數量,但是可以使用 COUNT 引數根據經驗進行調整。基本上,COUNT 引數的作用就是讓使用者告知迭代命令,在每次迭代中應該從資料集裡返回多少元素。雖然 COUNT 引數只是迭代命令實現上的一種提示(hint),但是在大多數情況下,這種提示是能滿足我們的預期:

  • COUNT 預設值為 10。
  • 在迭代一個足夠大、由雜湊表實現的資料庫、Set、Hash 或者 Sorted Set 時,如果使用者沒有使用 MATCH 引數,那麼每次呼叫返回 COUNT 個元素,或者比 COUNT 稍多的元素。
  • 在迭代一個編碼為 IntSet (一個只由整數值構成的小資料集) 或 Hash 的 Set 以及編碼為 ZipList (由不同值構成的小的 Hash 或者 Set) 的 Sorted Set 時,通常會無視 COUNT 引數指定的值,並在第一次呼叫時就將資料集包含的所有元素都返回給使用者。

沒有必要每次迭代都要使用相同的 COUNT 值。使用者可以在每次迭代中按自己的需要隨意改變 COUNT 值,只要記得將上次迭代返回的遊標用到下次迭代裡面就可以了。

6. MATCH引數

我們也可以通過匹配一個 Glob 風格的模式來迭代元素,類似於 KEYS 命令。我們只需要在 SCAN 命令後面追加 MATCH <pattern> 引數即可實現。

以下是一個使用 MATCH 引數進行迭代的示例:

redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
 2) "feelsgood"
 3) "foobar"
redis 127.0.0.1:6379>

我們需要注意的是 MATCH 過濾器是在從資料集中檢索出元素之後,在將資料返回給客戶端之前應用的。這意味著,如果模式匹配到資料集中很少的元素,則 SCAN 命令在很多次迭代中可能不返回元素。一個例子如下所示:

redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"
 2) "key:711"
 3) "key:118"
 4) "key:117"
 5) "key:311"
 6) "key:112"
 7) "key:111"
 8) "key:110"
 9) "key:113"
 10) "key:211"
 11) "key:411"
 12) "key:115"
 13) "key:116"
 14) "key:114"
 15) "key:119"
 16) "key:811"
 17) "key:511"
 18) "key:11"
redis 127.0.0.1:6379>

如上述所述,大多數呼叫沒有返回元素,而最後一次呼叫使用 COUNT 為1000,強制命令對該迭代進行更多掃描,從而使得命令返回的元素也變多了。

7. TYPE引數

從 6.0 版開始,我們可以使用此引數要求 SCAN 命令僅返回與給定型別匹配的物件,從而允許我們遍歷資料庫以查詢特定型別的鍵。SCAN 可以使用 TYPE 引數,但 HSCAN 或 ZSCAN 等不可用。

type 引數與 TYPE 命令返回的字串名稱相同。需要我們注意的是某些 Redis 型別(例如GeoHashes、HyperLogLogs、Bitmap 以及 Bitfields 等)其內部是使用其他 Redis 型別(例如 String 或 Zset)來實現的,因此 SCAN 命令無法將其與相同型別的其他鍵區分開。例如,ZSET 和 GEOHASH:

redis 127.0.0.1:6379> GEOADD geokey 0 0 value
(integer) 1
redis 127.0.0.1:6379> ZADD zkey 1000 value
(integer) 1
redis 127.0.0.1:6379> TYPE geokey
zset
redis 127.0.0.1:6379> TYPE zkey
zset
redis 127.0.0.1:6379> SCAN 0 TYPE zset
1) "0"
2) 1) "geokey"
 2) "zkey"

重要的是,TYPE 過濾器是在從資料庫中檢索元素之後應用的,因此該引數不會降低伺服器完成完整迭代所需的負載,對於稀有型別,我們可能不會收到任何元素。

8. 多次並行迭代

不同客戶端可能在同一時間迭代同一資料集,客戶端每次執行迭代都需要傳入一個遊標,並在迭代結束之後獲得一個新的遊標,而這個遊標就包含了迭代的所有狀態,因此,伺服器無須為迭代記錄任何狀態。

9. 在中間終止迭代

由於伺服器端不會記錄狀態,迭代的所有狀態都儲存在遊標中,因此呼叫方可以自由地中途終止迭代,不用向伺服器傳送通知。An infinite number of iterations can be started and never terminated without any issue.

10. 使用錯誤的遊標呼叫SCAN

使用錯誤的,負數的,超出範圍的遊標或其他無效的遊標來呼叫 SCAN,會導致未定義的行為,但絕不會導致崩潰。未定義的是指 SCAN 將不再確保返回元素的保證。

唯一有效的遊標是:

開始迭代時的遊標值為0。

上一次呼叫 SCAN 返回的遊標,以便繼續迭代。

11. 終止保證

只有在保證迭代的資料集大小始終保持在給定的最大上限內時(大小恆定),才能保證 SCAN 演算法能終止;否則,對一直增長的資料集進行迭代可能會導致 SCAN 永遠不會終止迭代(死迴圈)。

這很容易直觀地看出:如果資料集不斷增長,為了訪問所有可能出現的元素,將需要做越來越多的工作,而能否結束一個迭代取決於對 SCAN 的呼叫次數、COUNT 引數值以及資料集的增長速度。

12. 返回值

SCAN,SSCAN,HSCAN 以及 ZSCAN 命令都返回一個包含兩個元素的回覆,第一個元素表示遊標的無符號64位整數,第二個元素是迭代出的元素陣列:

SCAN 元素陣列是鍵的列表。

SSCAN 元素陣列是 Set 成員的列表。

HSCAN 元素陣列包含兩個元素,即欄位和值,對應 Hash 的每個返回元素。

ZSCAN 元素陣列包含兩個元素,即一個成員及其關聯的分數,對應 Sorted Set 中的每個返回元素。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。