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,如果資料量很大將會等待很久,因此,只適合用在可控的量小的鍵查詢,比如幾...