理解redis中的scan

2021-10-19 14:26:23 字數 4056 閱讀 4110

redis在2.8版本提供了scan相關命令用來遍歷集合中的元素。和keys,smembers命令遍歷大集合場景下會阻塞redis一定時間不同,scan命令每次遍歷只會返回一定數量集合元素和當前的遍歷位置的游標,時間非常短,不會阻塞redis,遍歷大集合時對其他業務影響較小。缺點是通過多次呼叫scan命令遍歷乙個集合會有資料的一致性的問題,後面的相關相關章節會解釋。scan家族相關命令有scan,sscan,hscan和zscan,區別如下:

下面是scan,sscan,hscan,zscan命令的語法:

scan cursor [match pattern] [count count] [type type]

sscan key cursor [match pattern] [count count]

hscan key cursor [match pattern] [count count]

zscan key cursor [match pattern] [count count]

cursor:游標,表示遍歷開始的位置,當游標為0時表示開始遍歷乙個集合,返回的游標值為0時表示集合遍歷已經遍歷完畢。

[match pattern]: 用來篩選匹配元素的正規表示式。

[count count]:可以指定每次掃瞄返回的元素的數量

[type type]:6.0版本新增的引數,只支援scan命令,表示掃瞄指定型別的key,key的型別可以用type命令檢視。

scan,sscan,hscan,zscan命令的返回值形式由兩部分組成:

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"

通過上面的介紹可以知道,我們是通過多次呼叫scan命令來遍歷乙個集合的,每次遍歷只會返回少量的元素。scan家族命令在做一次完整的集合遍歷會提供一下保證:

那麼小夥伴看到這裡可能會有疑問了,如果某個元素在遍歷中被移除了會發生什麼事情?莫急,下面就說說scan的缺點:

雖然預設scan返回元素數量預設是10個,但是返回的元素數量仍然是不確定的。在遍歷乙個大集合時可能會返回即幾十個元素,在遍歷內部使用編碼實現小集合(小的set,hash和sorted set)時會一次返回所有的元素。也可能返回乙個空集,但這並不表示遍歷完畢,當且僅當返回的游標值為0時才表示遍歷完畢。

如果先要固定返回指定數量的元素可以使用count選項

count選項用來指定每次scan應該返回的元素數量,但這是乙個暗示(hint)。一般來說count的工作原理如下:

match選項用來過濾scan的元素,而且是redis獲取本次scan的元素後,再過濾的。也就是說,redis仍然遍歷所有的元素,工作量和不指定match一樣。假如你指定match= product_id*表示只匹配以product_id開頭的元素,redis先從獲取到本次scan的元素,在返回客戶端之前過濾一遍。這樣導致的結果是,可能多次scan的都是空的,但這不意味著遍歷完畢了,只是本次遍歷的元素中沒有以product_id開頭的元素,只有返回游標為0時才表示遍歷結束。所以,在遍歷大集合中的少量特定元素時,多數情況都會返回空集合。如果指定每次都要求返回指定數量的匹配元素,可以使用count,但是這可能阻塞redis,那麼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>

type選項只能用於scan命令,表示遍歷特定型別的集合,它也是類似於count選項指定的正則過濾器一樣,只不過type這個過濾器過濾特定集合型別。和match選項一樣,redis獲取到本次遍歷的元素後,再對結果集過濾,所以多數情況下都會返回空集合。

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"

redis記錄遍歷的位置資訊,而是將本次遍歷資訊全部儲存在的cursor值中並返回客戶端。下一次scan時,redis根據cursor值重新定位到上一次遍歷的地方。redis本身不儲存遍歷的資訊。

因為遍歷的位置資訊儲存在cursor值並返回了客戶端,redis不儲存任何資訊,所以客戶端可以隨時終止遍歷,而無需與redis有任何互動。

如果使用了錯誤的cursor值(不是返回的cursor值),redis並不會報錯,那麼返回的值是不確定的,因為他定位的位置是不確定的。

scan只用當遍歷完整個集合才會結束(cursor=0)。因為redis是單執行緒的,當執行scan命令時,此時集合的大小確定,redis發現到達集合尾部時,遍歷就完成了(即使以後會往這個集合中新增資料)。但是如果集合快速增長,那麼可能永遠遍歷不完,這取決於集合增長的速率和你遍歷的速率(可以使用count)

scan命令利用cursor值確定上一次遍歷的位置,但是這是基於集合底層資料結構是hashtable(雜湊表)實現的。由於redis使用了一些記憶體優化的策略,在集合元素足夠小,足夠簡單時,使用inset(簡單的整數集合),ziplist(壓縮列表)儲存元素,使用cursor定位元素位置不可實現,所以便一次返回所有元素。當隨著集合元素增加時,redis會將集合的底層實現轉換為雜湊表,這時候count就又生效了。

Redis的Scan粗略理解

最近在了解redis的scan命令,scan就相當於分段遍歷,遍歷過程 現rehash也能保證scan不重複和不遺漏,這兩天了解這部分的原理,順便記錄一下。參考於redisscan迭代器遍歷操作原理 二 先說順序遍歷的問題,也就是按0,1,2,3 的順序,用例子說明,當遍歷完槽2的時候,返回下乙個遍...

Redis中的Scan命令的使用

redis中有乙個經典的問題,在巨大的資料量的情況下,做類似於查詢符合某種規則的key的資訊,這裡就有兩種方式,一是keys命令,簡單粗暴,由於redis單執行緒這一特性,keys命令是以阻塞的方式執行的,keys是以遍歷的方式實現的複雜度是 o n redis庫中的key越多,查詢實現代價越大,產...

redis中keys和scan的對比

redis中keys和scan的對比 兩者都是用來返回key的,但是使用場景和方法不同。一 keys keys pattern 比如 keys keys user info 特點 1 在選定的庫中,一次性全部返回符合條件的key,如果資料量很大將會等待很久,因此,只適合用在可控的量小的鍵查詢,比如幾...