Redis Scan 命令使用教程(高效遍歷海量數(shù)據(jù)的方法)
Redis 中 Scan 命令使用教程:高效遍歷海量數(shù)據(jù)
在 Redis 中,當(dāng)需要遍歷所有鍵或指定模式的鍵時(shí),傳統(tǒng)的 KEYS 命令會(huì)因阻塞主線程、無(wú)法分頁(yè)等問題,在海量數(shù)據(jù)場(chǎng)景下表現(xiàn)糟糕。而 Scan 命令憑借 “非阻塞”“分批遍歷” 的特性,成為解決大規(guī)模數(shù)據(jù)遍歷的最優(yōu)方案。本文將從基礎(chǔ)介紹、實(shí)現(xiàn)原理、實(shí)操方式到實(shí)踐總結(jié),全面講解 Scan 命令的使用。
一、介紹
Scan 是 Redis 2.8 版本引入的迭代式遍歷命令,主要用于遍歷 Redis 中的鍵集合或集合類型(如 Hash、Set、Sorted Set)的元素,核心目標(biāo)是解決傳統(tǒng)遍歷命令的性能問題。
1. 為什么需要 Scan?
傳統(tǒng)的 KEYS 命令存在明顯缺陷:
- 阻塞主線程:
KEYS會(huì)一次性遍歷所有符合條件的鍵,若數(shù)據(jù)量達(dá)百萬(wàn)級(jí),會(huì)占用大量 CPU 時(shí)間,導(dǎo)致 Redis 無(wú)法響應(yīng)其他請(qǐng)求; - 無(wú)分頁(yè)能力:
KEYS只能一次性返回所有結(jié)果,無(wú)法分批處理,容易造成客戶端內(nèi)存溢出; - 不支持復(fù)雜篩選:僅能通過簡(jiǎn)單的通配符(如
*、?)匹配,靈活性低。
而 Scan 命令恰好彌補(bǔ)這些不足,具備以下核心特性:
- 非阻塞:分批遍歷數(shù)據(jù),每次只處理少量元素,避免長(zhǎng)時(shí)間占用主線程;
- 游標(biāo)迭代:通過 “游標(biāo)(cursor)” 記錄遍歷位置,支持?jǐn)帱c(diǎn)續(xù)傳;
- 安全遍歷:遍歷過程中數(shù)據(jù)的新增、刪除、修改不會(huì)導(dǎo)致漏遍歷或重復(fù)遍歷(存在極小概率重復(fù),但可通過業(yè)務(wù)層去重解決);
- 多類型支持:除了遍歷所有鍵(
SCAN),還支持遍歷 Hash 字段(HSCAN)、Set 元素(SSCAN)、Sorted Set 元素(ZSCAN)。
2. 典型應(yīng)用場(chǎng)景
- 海量鍵統(tǒng)計(jì):如統(tǒng)計(jì) Redis 中所有以
user:為前綴的鍵數(shù)量; - 數(shù)據(jù)清理:分批刪除過期或無(wú)用的鍵(如刪除所有
temp:前綴的臨時(shí)鍵); - 集合元素遍歷:遍歷大型 Hash 中的所有字段值,避免一次性加載導(dǎo)致內(nèi)存溢出;
- 定期數(shù)據(jù)校驗(yàn):分批檢查鍵的過期時(shí)間或數(shù)據(jù)完整性。
二、使用原理
Scan 命令的核心是基于 “游標(biāo)” 和 “哈希表遍歷” 實(shí)現(xiàn)的,理解其底層原理能幫助更好地使用命令。
1. Redis 鍵空間的存儲(chǔ)結(jié)構(gòu)
Redis 的鍵空間(keyspace)底層基于哈希表存儲(chǔ),每個(gè)鍵通過哈希函數(shù)映射到哈希表的某個(gè) “桶(bucket)” 中。Scan 命令本質(zhì)是遍歷哈希表的桶,并通過游標(biāo)記錄當(dāng)前遍歷到的桶位置。
2. 游標(biāo)迭代機(jī)制
Scan 的遍歷過程類似 “翻書”,游標(biāo)就是 “頁(yè)碼”,具體流程如下:
- 初始游標(biāo):首次調(diào)用
Scan時(shí),游標(biāo)值設(shè)為0,表示從哈希表的起始位置開始遍歷; - 分批遍歷:Redis 會(huì)根據(jù)游標(biāo)位置,返回當(dāng)前批次的元素(默認(rèn) 10 個(gè)),并返回新的游標(biāo)值;
- 結(jié)束條件:當(dāng)返回的游標(biāo)值為
0時(shí),表示已遍歷完所有元素; - 斷點(diǎn)續(xù)傳:若遍歷中斷(如客戶端重啟),下次可使用上次返回的非 0 游標(biāo)繼續(xù)遍歷,無(wú)需從頭開始。
3. 避免漏遍歷與重復(fù)遍歷的設(shè)計(jì)
由于 Redis 哈希表在擴(kuò)容(rehash)時(shí)會(huì)重新分配桶的位置,Scan 通過以下機(jī)制保證遍歷的準(zhǔn)確性:
- 漸進(jìn)式 rehash 兼容:遍歷過程中若觸發(fā)哈希表擴(kuò)容,
Scan會(huì)同時(shí)遍歷舊哈希表和新哈希表,確保所有鍵都能被訪問到; - 允許重復(fù)遍歷:為了簡(jiǎn)化實(shí)現(xiàn),
Scan不保證元素只出現(xiàn)一次(尤其是在 rehash 過程中),但重復(fù)概率極低,業(yè)務(wù)層可通過去重(如用 Set 暫存結(jié)果)解決。
4. 計(jì)數(shù)參數(shù)(count)的作用
Scan 命令中的 count 參數(shù)用于指定 “每次遍歷的桶數(shù)量”,而非 “返回的元素?cái)?shù)量”:
- 默認(rèn)
count=10,表示每次遍歷 10 個(gè)桶,返回這些桶中的所有符合條件的元素; count并非嚴(yán)格限制,Redis 會(huì)根據(jù)桶中元素?cái)?shù)量動(dòng)態(tài)調(diào)整返回結(jié)果(如某個(gè)桶中沒有符合條件的鍵,可能返回少于count個(gè)元素);- 海量數(shù)據(jù)場(chǎng)景下,可適當(dāng)增大
count(如count=1000),減少遍歷次數(shù),提升效率。
三、使用方式
Scan 命令家族包括 SCAN(遍歷鍵空間)、HSCAN(遍歷 Hash)、SSCAN(遍歷 Set)、ZSCAN(遍歷 Sorted Set),核心用法類似,以下以最常用的 SCAN 為例講解,其他命令用法可類比。
1. 基礎(chǔ)語(yǔ)法
(1)SCAN 命令(遍歷所有鍵)
語(yǔ)法:SCAN cursor [MATCH pattern] [COUNT count] [TYPE type] cursor:游標(biāo)值(首次為 0,后續(xù)用上次返回的游標(biāo)) MATCH pattern:通配符匹配,篩選符合條件的鍵(可選) COUNT count:每次遍歷的桶數(shù)量(可選,默認(rèn) 10) TYPE type:按鍵類型篩選(如 string、hash、set,可選,Redis 6.0+ 支持)
(2)其他命令語(yǔ)法(類比)
HSCAN key cursor [MATCH pattern] [COUNT count]:遍歷 Hash 鍵key的字段和值;SSCAN key cursor [MATCH pattern] [COUNT count]:遍歷 Set 鍵key的元素;ZSCAN key cursor [MATCH pattern] [COUNT count]:遍歷 Sorted Set 鍵key的元素和分?jǐn)?shù)。
2. 實(shí)操示例
(1)遍歷所有鍵(無(wú)篩選)
# 首次遍歷:游標(biāo) 0,默認(rèn) count=10
127.0.0.1:6379> SCAN 0
1) "17" # 下次遍歷的游標(biāo)
2) 1) "user:1001"
2) "product:2003"
3) "order:5001"
# 本次返回 3 個(gè)鍵(少于 count=10,因部分桶無(wú)符合條件的鍵)
# 第二次遍歷:使用上次返回的游標(biāo) 17
127.0.0.1:6379> SCAN 17
1) "0" # 游標(biāo)為 0,遍歷結(jié)束
2) 1) "user:1002"
2) "temp:3001"(2)按前綴篩選鍵(MATCH)
# 遍歷所有以 "user:" 為前綴的鍵,count=20
127.0.0.1:6379> SCAN 0 MATCH user:* COUNT 20
1) "23"
2) 1) "user:1001"
2) "user:1002"
3) "user:1003"
# 繼續(xù)遍歷,直到游標(biāo)返回 0
127.0.0.1:6379> SCAN 23 MATCH user:* COUNT 20
1) "0"
2) 1) "user:1004"(3)按類型篩選鍵(TYPE,Redis 6.0+)
# 遍歷所有 string 類型的鍵
127.0.0.1:6379> SCAN 0 TYPE string
1) "12"
2) 1) "user:1001" # 假設(shè)該鍵是 string 類型
2) "temp:3001"(4)遍歷 Hash 鍵(HSCAN)
# 先創(chuàng)建一個(gè) Hash 鍵 127.0.0.1:6379> HMSET user:1001 name "Alice" age "25" city "Beijing" OK # 遍歷該 Hash 的字段和值 127.0.0.1:6379> HSCAN user:1001 0 1) "0" # 游標(biāo)為 0,Hash 元素少,一次遍歷完 2) 1) "name" 2) "Alice" 3) "age" 4) "25" 5) "city" 6) "Beijing"
3. 代碼示例(Golang)
以遍歷所有 user: 前綴的鍵為例,使用 go-redis 客戶端實(shí)現(xiàn):
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
)
func main() {
// 初始化 Redis 客戶端
client := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
defer client.Close()
ctx := context.Background()
// Scan 遍歷所有 user: 前綴的鍵
var cursor uint64 = 0 // 初始游標(biāo)
count := int64(20) // 每次遍歷的桶數(shù)量
pattern := "user:*" // 匹配模式
fmt.Println("開始遍歷 user: 前綴的鍵:")
for {
// 執(zhí)行 Scan 命令
result, err := client.Scan(ctx, cursor, pattern, count).Result()
if err != nil {
fmt.Printf("Scan 執(zhí)行失?。?v\n", err)
return
}
// 獲取本次結(jié)果和下次游標(biāo)
cursor = result.Cursor
keys := result.Keys
// 處理本次獲取的鍵
for _, key := range keys {
fmt.Printf("找到鍵:%s\n", key)
}
// 游標(biāo)為 0,遍歷結(jié)束
if cursor == 0 {
break
}
}
fmt.Println("遍歷完成")
}4. 注意事項(xiàng)
- 游標(biāo)必須正確傳遞:每次遍歷需使用上次返回的游標(biāo),否則會(huì)導(dǎo)致重復(fù)遍歷或漏遍歷;
- 避免過度依賴 MATCH:
MATCH是在遍歷結(jié)果中篩選,而非提前過濾,若符合條件的鍵極少,會(huì)導(dǎo)致多次空遍歷,建議結(jié)合業(yè)務(wù)場(chǎng)景優(yōu)化匹配模式; - count 參數(shù)按需調(diào)整:數(shù)據(jù)量小時(shí)用默認(rèn)
count=10即可,海量數(shù)據(jù)時(shí)可增大count(如 1000~10000),但不宜過大(避免單次操作耗時(shí)過長(zhǎng)); - 業(yè)務(wù)層去重:因
Scan可能返回重復(fù)元素,需在業(yè)務(wù)層通過 Set 或哈希表去重; - 不建議在主庫(kù)高頻使用:雖然
Scan非阻塞,但頻繁遍歷仍會(huì)占用 CPU 資源,建議在從庫(kù)執(zhí)行(需確保主從數(shù)據(jù)同步及時(shí))。
四、總結(jié)
Scan 命令是 Redis 中處理海量數(shù)據(jù)遍歷的核心工具,其核心優(yōu)勢(shì)可總結(jié)為:
- 非阻塞設(shè)計(jì):分批遍歷避免阻塞主線程,保障 Redis 服務(wù)可用性;
- 游標(biāo)迭代:支持?jǐn)帱c(diǎn)續(xù)傳,適合長(zhǎng)時(shí)間、大規(guī)模的遍歷任務(wù);
- 靈活篩選:通過
MATCH和TYPE實(shí)現(xiàn)精準(zhǔn)篩選,滿足多樣化需求; - 多類型支持:覆蓋鍵空間和所有集合類型,適用場(chǎng)景廣泛。
在實(shí)際使用中,需注意以下關(guān)鍵要點(diǎn):
- 正確傳遞游標(biāo),確保遍歷的連續(xù)性;
- 根據(jù)數(shù)據(jù)量調(diào)整
count參數(shù),平衡遍歷效率和資源占用; - 結(jié)合業(yè)務(wù)場(chǎng)景優(yōu)化篩選邏輯,減少無(wú)效遍歷;
- 對(duì)遍歷結(jié)果進(jìn)行去重,避免重復(fù)處理。
總之,Scan 命令徹底解決了傳統(tǒng) KEYS 命令的性能痛點(diǎn),是 Redis 運(yùn)維和開發(fā)中處理海量數(shù)據(jù)的 “必備工具”,掌握其用法能顯著提升大規(guī)模 Redis 集群的管理效率。
到此這篇關(guān)于Redis Scan 命令使用教程(高效遍歷海量數(shù)據(jù)的方法)的文章就介紹到這了,更多相關(guān)Redis Scan 命令使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
hiredis從安裝到項(xiàng)目實(shí)戰(zhàn)操作
這篇文章主要介紹了hiredis從安裝到項(xiàng)目實(shí)戰(zhàn)操作,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
基于redis實(shí)現(xiàn)分布式鎖的原理與方法
這篇文章主要給大家介紹了基于redis實(shí)現(xiàn)分布式鎖的原理與方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Redis 操作多個(gè)數(shù)據(jù)庫(kù)的配置的方法實(shí)現(xiàn)
本文主要介紹了Redis 操作多個(gè)數(shù)據(jù)庫(kù)的配置的方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
關(guān)于Redis最常見的十道面試題總結(jié)大全
Redis作為一個(gè)高性能的內(nèi)存數(shù)據(jù)存儲(chǔ)系統(tǒng),具有快速讀寫、持久性、數(shù)據(jù)結(jié)構(gòu)多樣性等特點(diǎn),廣泛應(yīng)用于各種應(yīng)用場(chǎng)景,這篇文章主要給大家介紹了關(guān)于Redis最常見的十道面試題總結(jié)的相關(guān)資料,需要的朋友可以參考下2024-07-07
Redis做數(shù)據(jù)持久化的解決方案及底層原理
Redis有兩種方式來(lái)實(shí)現(xiàn)數(shù)據(jù)的持久化,分別是RDB(Redis Database)和AOF(Append Only File),今天通過本文給大家聊一聊Redis做數(shù)據(jù)持久化的解決方案及底層原理,感興趣的朋友一起看看吧2021-07-07
Redis過期監(jiān)聽機(jī)制,訂單超時(shí)自動(dòng)取消方式
這篇文章主要介紹了Redis過期監(jiān)聽機(jī)制,訂單超時(shí)自動(dòng)取消方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Redis分布式鎖的正確實(shí)現(xiàn)方法總結(jié)
在本篇文章里小編給大家整理的是關(guān)于Redis分布式鎖的正確實(shí)現(xiàn)方式介紹,有興趣的朋友們可以學(xué)習(xí)下。2020-02-02

