Redis Scan 命令使用教程(高效遍歷海量數(shù)據(jù)的方法)
Redis 中 Scan 命令使用教程:高效遍歷海量數(shù)據(jù)
在 Redis 中,當(dāng)需要遍歷所有鍵或指定模式的鍵時,傳統(tǒng)的 KEYS 命令會因阻塞主線程、無法分頁等問題,在海量數(shù)據(jù)場景下表現(xiàn)糟糕。而 Scan 命令憑借 “非阻塞”“分批遍歷” 的特性,成為解決大規(guī)模數(shù)據(jù)遍歷的最優(yōu)方案。本文將從基礎(chǔ)介紹、實現(xiàn)原理、實操方式到實踐總結(jié),全面講解 Scan 命令的使用。
一、介紹
Scan 是 Redis 2.8 版本引入的迭代式遍歷命令,主要用于遍歷 Redis 中的鍵集合或集合類型(如 Hash、Set、Sorted Set)的元素,核心目標(biāo)是解決傳統(tǒng)遍歷命令的性能問題。
1. 為什么需要 Scan?
傳統(tǒng)的 KEYS 命令存在明顯缺陷:
- 阻塞主線程:
KEYS會一次性遍歷所有符合條件的鍵,若數(shù)據(jù)量達(dá)百萬級,會占用大量 CPU 時間,導(dǎo)致 Redis 無法響應(yīng)其他請求; - 無分頁能力:
KEYS只能一次性返回所有結(jié)果,無法分批處理,容易造成客戶端內(nèi)存溢出; - 不支持復(fù)雜篩選:僅能通過簡單的通配符(如
*、?)匹配,靈活性低。
而 Scan 命令恰好彌補這些不足,具備以下核心特性:
- 非阻塞:分批遍歷數(shù)據(jù),每次只處理少量元素,避免長時間占用主線程;
- 游標(biāo)迭代:通過 “游標(biāo)(cursor)” 記錄遍歷位置,支持?jǐn)帱c續(xù)傳;
- 安全遍歷:遍歷過程中數(shù)據(jù)的新增、刪除、修改不會導(dǎo)致漏遍歷或重復(fù)遍歷(存在極小概率重復(fù),但可通過業(yè)務(wù)層去重解決);
- 多類型支持:除了遍歷所有鍵(
SCAN),還支持遍歷 Hash 字段(HSCAN)、Set 元素(SSCAN)、Sorted Set 元素(ZSCAN)。
2. 典型應(yīng)用場景
- 海量鍵統(tǒng)計:如統(tǒng)計 Redis 中所有以
user:為前綴的鍵數(shù)量; - 數(shù)據(jù)清理:分批刪除過期或無用的鍵(如刪除所有
temp:前綴的臨時鍵); - 集合元素遍歷:遍歷大型 Hash 中的所有字段值,避免一次性加載導(dǎo)致內(nèi)存溢出;
- 定期數(shù)據(jù)校驗:分批檢查鍵的過期時間或數(shù)據(jù)完整性。
二、使用原理
Scan 命令的核心是基于 “游標(biāo)” 和 “哈希表遍歷” 實現(xiàn)的,理解其底層原理能幫助更好地使用命令。
1. Redis 鍵空間的存儲結(jié)構(gòu)
Redis 的鍵空間(keyspace)底層基于哈希表存儲,每個鍵通過哈希函數(shù)映射到哈希表的某個 “桶(bucket)” 中。Scan 命令本質(zhì)是遍歷哈希表的桶,并通過游標(biāo)記錄當(dāng)前遍歷到的桶位置。
2. 游標(biāo)迭代機制
Scan 的遍歷過程類似 “翻書”,游標(biāo)就是 “頁碼”,具體流程如下:
- 初始游標(biāo):首次調(diào)用
Scan時,游標(biāo)值設(shè)為0,表示從哈希表的起始位置開始遍歷; - 分批遍歷:Redis 會根據(jù)游標(biāo)位置,返回當(dāng)前批次的元素(默認(rèn) 10 個),并返回新的游標(biāo)值;
- 結(jié)束條件:當(dāng)返回的游標(biāo)值為
0時,表示已遍歷完所有元素; - 斷點續(xù)傳:若遍歷中斷(如客戶端重啟),下次可使用上次返回的非 0 游標(biāo)繼續(xù)遍歷,無需從頭開始。
3. 避免漏遍歷與重復(fù)遍歷的設(shè)計
由于 Redis 哈希表在擴容(rehash)時會重新分配桶的位置,Scan 通過以下機制保證遍歷的準(zhǔn)確性:
- 漸進式 rehash 兼容:遍歷過程中若觸發(fā)哈希表擴容,
Scan會同時遍歷舊哈希表和新哈希表,確保所有鍵都能被訪問到; - 允許重復(fù)遍歷:為了簡化實現(xiàn),
Scan不保證元素只出現(xiàn)一次(尤其是在 rehash 過程中),但重復(fù)概率極低,業(yè)務(wù)層可通過去重(如用 Set 暫存結(jié)果)解決。
4. 計數(shù)參數(shù)(count)的作用
Scan 命令中的 count 參數(shù)用于指定 “每次遍歷的桶數(shù)量”,而非 “返回的元素數(shù)量”:
- 默認(rèn)
count=10,表示每次遍歷 10 個桶,返回這些桶中的所有符合條件的元素; count并非嚴(yán)格限制,Redis 會根據(jù)桶中元素數(shù)量動態(tài)調(diào)整返回結(jié)果(如某個桶中沒有符合條件的鍵,可能返回少于count個元素);- 海量數(shù)據(jù)場景下,可適當(dāng)增大
count(如count=1000),減少遍歷次數(shù),提升效率。
三、使用方式
Scan 命令家族包括 SCAN(遍歷鍵空間)、HSCAN(遍歷 Hash)、SSCAN(遍歷 Set)、ZSCAN(遍歷 Sorted Set),核心用法類似,以下以最常用的 SCAN 為例講解,其他命令用法可類比。
1. 基礎(chǔ)語法
(1)SCAN 命令(遍歷所有鍵)
語法: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)其他命令語法(類比)
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. 實操示例
(1)遍歷所有鍵(無篩選)
# 首次遍歷:游標(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 個鍵(少于 count=10,因部分桶無符合條件的鍵)
# 第二次遍歷:使用上次返回的游標(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)建一個 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 客戶端實現(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. 注意事項
- 游標(biāo)必須正確傳遞:每次遍歷需使用上次返回的游標(biāo),否則會導(dǎo)致重復(fù)遍歷或漏遍歷;
- 避免過度依賴 MATCH:
MATCH是在遍歷結(jié)果中篩選,而非提前過濾,若符合條件的鍵極少,會導(dǎo)致多次空遍歷,建議結(jié)合業(yè)務(wù)場景優(yōu)化匹配模式; - count 參數(shù)按需調(diào)整:數(shù)據(jù)量小時用默認(rèn)
count=10即可,海量數(shù)據(jù)時可增大count(如 1000~10000),但不宜過大(避免單次操作耗時過長); - 業(yè)務(wù)層去重:因
Scan可能返回重復(fù)元素,需在業(yè)務(wù)層通過 Set 或哈希表去重; - 不建議在主庫高頻使用:雖然
Scan非阻塞,但頻繁遍歷仍會占用 CPU 資源,建議在從庫執(zhí)行(需確保主從數(shù)據(jù)同步及時)。
四、總結(jié)
Scan 命令是 Redis 中處理海量數(shù)據(jù)遍歷的核心工具,其核心優(yōu)勢可總結(jié)為:
- 非阻塞設(shè)計:分批遍歷避免阻塞主線程,保障 Redis 服務(wù)可用性;
- 游標(biāo)迭代:支持?jǐn)帱c續(xù)傳,適合長時間、大規(guī)模的遍歷任務(wù);
- 靈活篩選:通過
MATCH和TYPE實現(xiàn)精準(zhǔn)篩選,滿足多樣化需求; - 多類型支持:覆蓋鍵空間和所有集合類型,適用場景廣泛。
在實際使用中,需注意以下關(guān)鍵要點:
- 正確傳遞游標(biāo),確保遍歷的連續(xù)性;
- 根據(jù)數(shù)據(jù)量調(diào)整
count參數(shù),平衡遍歷效率和資源占用; - 結(jié)合業(yè)務(wù)場景優(yōu)化篩選邏輯,減少無效遍歷;
- 對遍歷結(jié)果進行去重,避免重復(fù)處理。
總之,Scan 命令徹底解決了傳統(tǒng) KEYS 命令的性能痛點,是 Redis 運維和開發(fā)中處理海量數(shù)據(jù)的 “必備工具”,掌握其用法能顯著提升大規(guī)模 Redis 集群的管理效率。
到此這篇關(guān)于Redis Scan 命令使用教程(高效遍歷海量數(shù)據(jù)的方法)的文章就介紹到這了,更多相關(guān)Redis Scan 命令使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis 操作多個數(shù)據(jù)庫的配置的方法實現(xiàn)
本文主要介紹了Redis 操作多個數(shù)據(jù)庫的配置的方法實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
關(guān)于Redis最常見的十道面試題總結(jié)大全
Redis作為一個高性能的內(nèi)存數(shù)據(jù)存儲系統(tǒng),具有快速讀寫、持久性、數(shù)據(jù)結(jié)構(gòu)多樣性等特點,廣泛應(yīng)用于各種應(yīng)用場景,這篇文章主要給大家介紹了關(guān)于Redis最常見的十道面試題總結(jié)的相關(guān)資料,需要的朋友可以參考下2024-07-07
Redis做數(shù)據(jù)持久化的解決方案及底層原理
Redis有兩種方式來實現(xiàn)數(shù)據(jù)的持久化,分別是RDB(Redis Database)和AOF(Append Only File),今天通過本文給大家聊一聊Redis做數(shù)據(jù)持久化的解決方案及底層原理,感興趣的朋友一起看看吧2021-07-07

