1. 概述
SCAN 命令以及比較相近的 SSCAN、HSCAN 和 ZSCAN 命令都用于增量迭代數(shù)據(jù)集元素:
- SCAN 命令用于迭代當(dāng)前數(shù)據(jù)庫中的數(shù)據(jù)庫鍵。
- SSCAN 命令用于迭代集合(Set)中的元素。
- HSCAN 命令用于迭代哈希(Hash)中的字段以及對(duì)應(yīng)的值。
- ZSCAN 命令用于迭代有序集合(Sorted Set)中的元素以及對(duì)應(yīng)的得分。
由于這些命令都可以增量迭代,每次調(diào)用都只會(huì)返回少量元素,所以這些命令可以用于生產(chǎn)環(huán)境中,不用擔(dān)心像使用 KEYS、SMEMBERS 命令帶來的問題。在鍵或元素的大數(shù)據(jù)集上調(diào)用這些命令可能會(huì)長(zhǎng)時(shí)間(甚至幾秒鐘)阻塞服務(wù)器。像 SMEMBERS 這樣的阻塞命令能夠在給定的時(shí)間內(nèi)提供數(shù)據(jù)集中所有的元素,但 SCAN 系列命令僅對(duì)返回的元素提供有限的保證,因?yàn)閿?shù)據(jù)集在我們?cè)隽康鷷r(shí)可能會(huì)發(fā)生改變。
SCAN,SSCAN,HSCAN 以及 ZSCAN 命令工作原理都非常類似,因此這篇文章會(huì)涵蓋這四個(gè)命令。區(qū)別在于 SSCAN,HSCAN 以及 ZSCAN 命令,第一個(gè)參數(shù)是保存 Set,Hash或 Sorted Set 值的鍵的名稱。SCAN命令不需要任何鍵名參數(shù),因?yàn)樗鼤?huì)迭代當(dāng)前數(shù)據(jù)庫中所有的鍵,因此迭代的對(duì)象是數(shù)據(jù)庫本身。
2. 基本用法
SCAN 是基于游標(biāo)的迭代器。這意味著在每次調(diào)用該命令時(shí),服務(wù)器都會(huì)返回一個(gè)更新后的新游標(biāo),用戶需要在下一次調(diào)用中將這個(gè)新游標(biāo)作為 SCAN 命令的游標(biāo)參數(shù)。當(dāng) SCAN 命令的游標(biāo)參數(shù)被設(shè)置為 0 時(shí), 服務(wù)器將開始一次新的迭代,而當(dāng)服務(wù)器向用戶返回的新游標(biāo)為 0 時(shí)會(huì)終止迭代。以下是 SCAN 迭代的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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" |
在上面的示例中,第一次調(diào)用使用 0 作為游標(biāo)來開始一次新的迭代。第二次調(diào)用時(shí)使用上一次調(diào)用返回的游標(biāo),即命令回復(fù)的第一個(gè)元素值,即17。從上面的示例可以看到,SCAN 命令返回值是兩個(gè)值的數(shù)組:第一個(gè)值是下一次調(diào)用中將要使用的新游標(biāo),第二個(gè)值是包含返回元素的數(shù)組。
由于在第二次調(diào)用中返回的游標(biāo)為 0,因此服務(wù)器向調(diào)用者發(fā)送信號(hào),告知迭代已完成,并且遍歷完數(shù)據(jù)集。從游標(biāo)值 0 開始迭代,然后調(diào)用 SCAN 直到返回的游標(biāo)再次為 0,表示一個(gè)完整迭代。
3. 保證
SCAN 命令,以及其他增量迭代命令,在整個(gè)完整迭代過程中可以為用戶提供一系列的保證:
- 在完整迭代開始直到完整迭代結(jié)束期間內(nèi)的所有元素都會(huì)被遍歷返回;這意味著,如果某個(gè)給定元素在開始迭代時(shí)位于數(shù)據(jù)集內(nèi),并且在終止迭代時(shí)仍然存在,那么 SCAN 會(huì)在某次迭代時(shí)返回給用戶。
- 在完整迭代開始直到完整迭代結(jié)束期間內(nèi)不存在的元素永遠(yuǎn)都不會(huì)被返回;因此,如果某個(gè)元素在迭代開始之前就被刪除,并且在后續(xù)的迭代過程中從未添加回?cái)?shù)據(jù)集中,那么 SCAN 永遠(yuǎn)都不會(huì)返回該元素 。
但是,由于 SCAN 只有很少的關(guān)聯(lián)狀態(tài)(僅有游標(biāo)),因此具有以下缺點(diǎn):
- 同一個(gè)元素可能會(huì)被返回多次。重復(fù)元素的問題需要我們自己的應(yīng)用程序處理, 例如,可以考慮將迭代返回的元素用于冪等操作(可以重復(fù)執(zhí)行多次操作)上。
- 如果一個(gè)元素是在迭代過程中被添加到數(shù)據(jù)集的,又或者是在迭代過程中從數(shù)據(jù)集中被刪除的,那么這個(gè)元素可能會(huì)被返回,也可能不會(huì)。
4. 每次執(zhí)行返回?cái)?shù)量
SCAN 系列的函數(shù)不能保證每次調(diào)用返回的元素?cái)?shù)量會(huì)在給定范圍內(nèi)。每次調(diào)用可能會(huì)返回 0 個(gè)元素,但只要返回的游標(biāo)不為 0,客戶端就認(rèn)為迭代沒有結(jié)束(即使返回了 0 個(gè)元素也不能表示迭代的結(jié)束)。返回的元素?cái)?shù)量會(huì)符合一定的規(guī)則:
- 在迭代大型數(shù)據(jù)集時(shí),SCAN 最多可能會(huì)返回幾十個(gè)元素。
- 在迭代小的數(shù)據(jù)集并且內(nèi)部為編碼數(shù)據(jù)結(jié)構(gòu)時(shí)(小的 Set、Hashe 以及 Sorted Set),單次調(diào)用就可以返回?cái)?shù)據(jù)集的所有元素。
但是,用戶可以使用 COUNT 參數(shù)來調(diào)整每次調(diào)用返回的元素的數(shù)量級(jí)。
5. COUNT參數(shù)
雖然 SCAN 不能保證每次迭代返回的元素?cái)?shù)量,但是可以使用 COUNT 參數(shù)根據(jù)經(jīng)驗(yàn)進(jìn)行調(diào)整。基本上,COUNT 參數(shù)的作用就是讓用戶告知迭代命令,在每次迭代中應(yīng)該從數(shù)據(jù)集里返回多少元素。雖然 COUNT 參數(shù)只是迭代命令實(shí)現(xiàn)上的一種提示(hint),但是在大多數(shù)情況下,這種提示是能滿足我們的預(yù)期:
- COUNT 默認(rèn)值為 10。
- 在迭代一個(gè)足夠大、由哈希表實(shí)現(xiàn)的數(shù)據(jù)庫、Set、Hash 或者 Sorted Set 時(shí),如果用戶沒有使用 MATCH 參數(shù),那么每次調(diào)用返回 COUNT 個(gè)元素,或者比 COUNT 稍多的元素。
- 在迭代一個(gè)編碼為 IntSet (一個(gè)只由整數(shù)值構(gòu)成的小數(shù)據(jù)集) 或 Hash 的 Set 以及編碼為 ZipList (由不同值構(gòu)成的小的 Hash 或者 Set) 的 Sorted Set 時(shí),通常會(huì)無視 COUNT 參數(shù)指定的值,并在第一次調(diào)用時(shí)就將數(shù)據(jù)集包含的所有元素都返回給用戶。
沒有必要每次迭代都要使用相同的 COUNT 值。用戶可以在每次迭代中按自己的需要隨意改變 COUNT 值,只要記得將上次迭代返回的游標(biāo)用到下次迭代里面就可以了。
6. MATCH參數(shù)
我們也可以通過匹配一個(gè) Glob 風(fēng)格的模式來迭代元素,類似于 KEYS 命令。我們只需要在 SCAN 命令后面追加 MATCH <pattern> 參數(shù)即可實(shí)現(xiàn)。
以下是一個(gè)使用 MATCH 參數(shù)進(jìn)行迭代的示例:
1
2
3
4
5
6
7
8
|
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 過濾器是在從數(shù)據(jù)集中檢索出元素之后,在將數(shù)據(jù)返回給客戶端之前應(yīng)用的。這意味著,如果模式匹配到數(shù)據(jù)集中很少的元素,則 SCAN 命令在很多次迭代中可能不返回元素。一個(gè)例子如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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 > |
如上述所述,大多數(shù)調(diào)用沒有返回元素,而最后一次調(diào)用使用 COUNT 為1000,強(qiáng)制命令對(duì)該迭代進(jìn)行更多掃描,從而使得命令返回的元素也變多了。
7. TYPE參數(shù)
從 6.0 版開始,我們可以使用此參數(shù)要求 SCAN 命令僅返回與給定類型匹配的對(duì)象,從而允許我們遍歷數(shù)據(jù)庫以查找特定類型的鍵。SCAN 可以使用 TYPE 參數(shù),但 HSCAN 或 ZSCAN 等不可用。
type 參數(shù)與 TYPE 命令返回的字符串名稱相同。需要我們注意的是某些 Redis 類型(例如GeoHashes、HyperLogLogs、Bitmap 以及 Bitfields 等)其內(nèi)部是使用其他 Redis 類型(例如 String 或 Zset)來實(shí)現(xiàn)的,因此 SCAN 命令無法將其與相同類型的其他鍵區(qū)分開。例如,ZSET 和 GEOHASH:
1
2
3
4
5
6
7
8
9
10
11
12
|
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 過濾器是在從數(shù)據(jù)庫中檢索元素之后應(yīng)用的,因此該參數(shù)不會(huì)降低服務(wù)器完成完整迭代所需的負(fù)載,對(duì)于稀有類型,我們可能不會(huì)收到任何元素。
8. 多次并行迭代
不同客戶端可能在同一時(shí)間迭代同一數(shù)據(jù)集,客戶端每次執(zhí)行迭代都需要傳入一個(gè)游標(biāo),并在迭代結(jié)束之后獲得一個(gè)新的游標(biāo),而這個(gè)游標(biāo)就包含了迭代的所有狀態(tài),因此,服務(wù)器無須為迭代記錄任何狀態(tài)。
9. 在中間終止迭代
由于服務(wù)器端不會(huì)記錄狀態(tài),迭代的所有狀態(tài)都保存在游標(biāo)中,因此調(diào)用方可以自由地中途終止迭代,不用向服務(wù)器發(fā)送通知。An infinite number of iterations can be started and never terminated without any issue.
10. 使用錯(cuò)誤的游標(biāo)調(diào)用SCAN
使用錯(cuò)誤的,負(fù)數(shù)的,超出范圍的游標(biāo)或其他無效的游標(biāo)來調(diào)用 SCAN,會(huì)導(dǎo)致未定義的行為,但絕不會(huì)導(dǎo)致崩潰。未定義的是指 SCAN 將不再確保返回元素的保證。
唯一有效的游標(biāo)是:
開始迭代時(shí)的游標(biāo)值為0。
上一次調(diào)用 SCAN 返回的游標(biāo),以便繼續(xù)迭代。
11. 終止保證
只有在保證迭代的數(shù)據(jù)集大小始終保持在給定的最大上限內(nèi)時(shí)(大小恒定),才能保證 SCAN 算法能終止;否則,對(duì)一直增長(zhǎng)的數(shù)據(jù)集進(jìn)行迭代可能會(huì)導(dǎo)致 SCAN 永遠(yuǎn)不會(huì)終止迭代(死循環(huán))。
這很容易直觀地看出:如果數(shù)據(jù)集不斷增長(zhǎng),為了訪問所有可能出現(xiàn)的元素,將需要做越來越多的工作,而能否結(jié)束一個(gè)迭代取決于對(duì) SCAN 的調(diào)用次數(shù)、COUNT 參數(shù)值以及數(shù)據(jù)集的增長(zhǎng)速度。
12. 返回值
SCAN,SSCAN,HSCAN 以及 ZSCAN 命令都返回一個(gè)包含兩個(gè)元素的回復(fù),第一個(gè)元素表示游標(biāo)的無符號(hào)64位整數(shù),第二個(gè)元素是迭代出的元素?cái)?shù)組:
SCAN 元素?cái)?shù)組是鍵的列表。
SSCAN 元素?cái)?shù)組是 Set 成員的列表。
HSCAN 元素?cái)?shù)組包含兩個(gè)元素,即字段和值,對(duì)應(yīng) Hash 的每個(gè)返回元素。
ZSCAN 元素?cái)?shù)組包含兩個(gè)元素,即一個(gè)成員及其關(guān)聯(lián)的分?jǐn)?shù),對(duì)應(yīng) Sorted Set 中的每個(gè)返回元素。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:https://mp.weixin.qq.com/s?__biz=MzI0OTYwNTQ4Ng==&mid=2247484202&idx=1&sn=be7f897500d0cfa237515fe4379c61e4