redis中數(shù)據(jù)模糊查詢scan用法詳解
redis中數(shù)據(jù)模糊查找-scan用法
1.查找方法
Redis中有一個(gè)經(jīng)典的問題,在巨大的數(shù)據(jù)量的情況下,做類似于查找符合某種規(guī)則的Key的信息,這里就有兩種方式,
一是keys命令,簡單粗暴,由于Redis單線程這一特性,keys命令是以阻塞的方式執(zhí)行的,keys是以遍歷的方式實(shí)現(xiàn)的復(fù)雜度是 O(n),Redis庫中的key越多,查找實(shí)現(xiàn)代價(jià)越大,產(chǎn)生的阻塞時(shí)間越長。
二是scan命令,以非阻塞的方式實(shí)現(xiàn)key值的查找,絕大多數(shù)情況下是可以替代keys命令的,可選性更強(qiáng)
2.keys命令
127.0.0.1:6379> keys s* 1) "s1" 2) "sddddf" 3) "sss" 4) "s358" 127.0.0.1:6379>
3.scan命令
SCAN cursor [MATCH pattern] [COUNT count] cursor - 游標(biāo)。 pattern - 匹配的模式。 count - 指定從數(shù)據(jù)集里返回多少元素,默認(rèn)值為 10 。 可用版本 >= 2.8.0
示例:
127.0.0.1:6379> keys s* 1) "s1" 2) "sddddf" 3) "sss" 4) "s358" 127.0.0.1:6379> scan 0 match s* count 20 1) "0" 2) 1) "s1" 2) "sss" 3) "s358" 4) "sddddf" 127.0.0.1:6379> scan 0 match s* count 2 1) "4" 2) 1) "s1" 127.0.0.1:6379> scan 4 match s* count 2 1) "1" 2) 1) "sss" 2) "s358" 127.0.0.1:6379> scan 1 match s* count 2 1) "3" 2) 1) "sddddf"
SCAN 命令是一個(gè)基于游標(biāo)的迭代器,每次被調(diào)用之后, 都會(huì)向用戶返回一個(gè)新的游標(biāo), 用戶在下次迭代時(shí)需要使用這個(gè)新游標(biāo)作為 SCAN 命令的游標(biāo)參數(shù), 以此來延續(xù)之前的迭代過程。
4.Java代碼中的實(shí)現(xiàn)
public Set<String> scan(String matchKey) {
Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
Set<String> keysTmp = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(
new ScanOptions.ScanOptionsBuilder().match("*" + matchKey + "*").count(1000).build()
);
while (cursor.hasNext()) {
keysTmp.add(new String(cursor.next()));
}
return keysTmp;
});
return keys;
}redis的模糊查詢提高效率
Redis 中進(jìn)行模糊查詢(例如使用 KEYS pattern 或 SCAN cursor MATCH pattern)本質(zhì)上效率是相對較低的,尤其是在大型數(shù)據(jù)集上,因?yàn)?Redis 的核心優(yōu)勢在于通過精確鍵進(jìn)行 O(1) 復(fù)雜度的快速訪問,而模糊查詢通常需要遍歷鍵空間。
提高 Redis 模糊查詢效率的核心策略是:避免在運(yùn)行時(shí)進(jìn)行全鍵空間掃描!
以下是一些有效的方法來提高效率或規(guī)避效率瓶頸:
?? 1. 避免使用 KEYS 命令 (最重要!)
- 問題:
KEYS命令會(huì)阻塞 Redis 服務(wù),直到遍歷完所有鍵并返回匹配結(jié)果。在生產(chǎn)環(huán)境的大數(shù)據(jù)集上,這可能導(dǎo)致服務(wù)不可用。 - 解決方案: 絕對禁止 在生產(chǎn)環(huán)境使用
KEYS。使用SCAN替代。
?? 2. 使用 SCAN 命令進(jìn)行迭代式查詢
原理: SCAN 命令使用游標(biāo)(cursor)進(jìn)行迭代,每次只返回一小部分匹配的鍵。它不會(huì)阻塞服務(wù)器,因?yàn)槊看握{(diào)用只占用少量時(shí)間。
優(yōu)點(diǎn):
- 非阻塞: 不會(huì)導(dǎo)致服務(wù)停頓。
- 增量式: 可以分批處理結(jié)果,減輕客戶端和服務(wù)端壓力。
缺點(diǎn):
- 不是原子快照: 在迭代過程中,如果鍵空間發(fā)生變化(增、刪、改),可能會(huì)看到重復(fù)的鍵或遺漏部分鍵。這通??梢越邮?。
- 整體耗時(shí)可能不短: 雖然每次調(diào)用快,但要獲取所有匹配結(jié)果,最終需要完成的“工作總量”和
KEYS類似(都需要遍歷大部分或全部鍵空間)。 - 客戶端邏輯復(fù)雜: 需要管理游標(biāo)和循環(huán)。
用法:
SCAN 0 MATCH user:profile:*:email COUNT 100
0 是起始游標(biāo)(第一次調(diào)用)。
MATCH pattern 指定模糊匹配模式(可選)。
COUNT n 建議每次迭代返回的元素?cái)?shù)量(只是個(gè)提示,Redis 可能返回更多或更少)。適當(dāng)增加 COUNT (如 500, 1000) 可以在網(wǎng)絡(luò)往返次數(shù)和單次耗時(shí)之間取得平衡,提高整體效率。
變種: SSCAN (掃描 Set), HSCAN (掃描 Hash), ZSCAN (掃描 Sorted Set)。這些用于掃描特定鍵內(nèi)部的大集合元素,避免阻塞或大結(jié)果集。
?? 3. 設(shè)計(jì)可查詢的鍵結(jié)構(gòu) (最重要的優(yōu)化方向!)
核心思想是將運(yùn)行時(shí)掃描轉(zhuǎn)化為精確查找或小范圍查找。這通常需要犧牲一些存儲(chǔ)空間(空間換時(shí)間)和增加寫入/更新時(shí)的維護(hù)成本。
- a) 使用索引集合 (Index Set):
- 場景: 查詢具有特定前綴、后綴或中間部分的鍵(如
user:123:profile,order:abc:details)。 - 方法:
- 創(chuàng)建一個(gè)專門的 Set 類型鍵(如
index:user:ids)。 - 每當(dāng)創(chuàng)建一個(gè)新用戶鍵(如
SET user:123:profile {...}),同時(shí)將123添加到索引集合(SADD index:user:ids 123)。 - 當(dāng)需要查詢所有用戶鍵時(shí),使用
SMEMBERS index:user:ids或SSCAN index:user:ids獲取所有用戶 ID。 - 客戶端拿到 ID 列表后,再通過精確鍵(
GET user:<id>:profile)獲取數(shù)據(jù)。
- 創(chuàng)建一個(gè)專門的 Set 類型鍵(如
- 優(yōu)點(diǎn): 獲取鍵列表非??欤∣(1) 或 O(N),N 是用戶數(shù)而非總鍵數(shù)),避免了全鍵掃描。
- 缺點(diǎn): 需要維護(hù)索引;占用額外內(nèi)存;獲取完整數(shù)據(jù)需要多次查詢(N+1 問題)。
- 場景: 查詢具有特定前綴、后綴或中間部分的鍵(如
- b) 使用 Sorted Set 按模式存儲(chǔ)鍵或引用:
- 場景: 需要按范圍(如時(shí)間范圍、分?jǐn)?shù)范圍)查詢,或者需要排序。
- 方法:
- 創(chuàng)建一個(gè) Sorted Set 鍵(如
zindex:orders:by_time)。 - 成員(member)可以是:
- 完整的鍵名(如
order:abc:details) - 適用于鍵名本身包含信息(如時(shí)間戳)。 - 或一個(gè)唯一 ID(如
abc),分?jǐn)?shù)(score)是查詢依據(jù)(如訂單創(chuàng)建時(shí)間戳)。
- 完整的鍵名(如
- 當(dāng)需要查詢某時(shí)間段內(nèi)的訂單:
- 使用
ZRANGEBYSCORE zindex:orders:by_time start_timestamp end_timestamp獲取鍵名或 ID。 - 再通過精確鍵獲取數(shù)據(jù)。
- 使用
- 創(chuàng)建一個(gè) Sorted Set 鍵(如
- 優(yōu)點(diǎn): 支持高效的范圍查詢和排序。
- 缺點(diǎn): 維護(hù)索引;額外內(nèi)存;潛在 N+1 問題。
使用 Hash 存儲(chǔ)子字段索引:
場景: 需要根據(jù)對象內(nèi)部字段的值進(jìn)行查詢(如查找所有
email以@gmail.com結(jié)尾的用戶)。方法:
創(chuàng)建輔助數(shù)據(jù)結(jié)構(gòu):
反向索引(Inverted Index): 對于需要查詢的字段值(如郵箱后綴
gmail.com),創(chuàng)建一個(gè) Set(如index:email_suffix:gmail.com),存儲(chǔ)擁有該后綴的用戶 ID。
更新數(shù)據(jù)時(shí):
修改用戶 Hash (
HSET user:123 email new@domain.com)。將用戶
123從舊后綴索引集移除(SREM index:email_suffix:old.com 123)。將用戶
123添加到新后綴索引集(SADD index:email_suffix:new.com 123)。
查詢時(shí):
SMEMBERS index:email_suffix:gmail.com獲取用戶 ID 列表,然后HGETALL user:<id>。優(yōu)點(diǎn): 對于特定字段的等值查詢非常高效。
缺點(diǎn): 維護(hù)成本最高(尤其字段值頻繁更新時(shí));占用大量額外內(nèi)存;只適用于等值查詢或有限模式(后綴=
SADD時(shí)存儲(chǔ)后綴);N+1 問題。
d) 拆分鍵名 + 利用集合操作:
場景: 鍵名由多個(gè)部分組成(如
country:region:city:userid),需要按不同層級查詢。方法:
為每個(gè)層級維護(hù)索引集:
countries = { 'us', 'uk', 'jp' ... }regions:us = { 'ca', 'ny', 'tx' ... }cities:us:ca = { 'sf', 'la', 'sd' ... }
查詢用戶時(shí):
先通過精確鍵獲取國家列表、特定國家的地區(qū)列表、特定國家地區(qū)的城市列表。
然后構(gòu)造出所有可能的鍵前綴(如
us:ca:sf)。最后用
SSCAN遍歷user:us:ca:sf:*(范圍大大縮小)。
優(yōu)點(diǎn): 將全鍵掃描縮小到特定小范圍掃描。
缺點(diǎn): 需要精心設(shè)計(jì)鍵結(jié)構(gòu)和索引;維護(hù)索引;可能仍需小范圍
SCAN。
?? 4. 緩存模糊查詢結(jié)果
- 場景: 模糊查詢模式相對固定且結(jié)果變化不頻繁(如查詢所有“活躍用戶”列表)。
- 方法: 定期(如每分鐘)或在數(shù)據(jù)變更時(shí)觸發(fā)一次
SCAN,將結(jié)果存入一個(gè) Redis Set 或 List 中。 - 查詢時(shí): 直接讀取這個(gè)緩存的結(jié)果集合。
- 優(yōu)點(diǎn): 查詢速度極快(O(1) 或 O(N) 讀集合)。
- 缺點(diǎn): 數(shù)據(jù)不是實(shí)時(shí)最新(最終一致性);需要維護(hù)緩存更新邏輯;占用額外內(nèi)存。
?? 5. 使用外部索引/搜索引擎 (對于復(fù)雜查詢或海量數(shù)據(jù))
- 原理: 當(dāng) Redis 內(nèi)置的查詢能力(即使是優(yōu)化后的索引)無法滿足復(fù)雜模式匹配(如全文搜索、多字段組合查詢)或數(shù)據(jù)量極大時(shí)。
- 工具: Redis 官方模塊 RediSearch, Elasticsearch, Solr, OpenSearch 等。
- 方法:
- 數(shù)據(jù)寫入/更新 Redis 的同時(shí),異步寫入索引到搜索引擎。
- 查詢請求發(fā)送到搜索引擎,獲取匹配的鍵 ID 或文檔。
- 根據(jù) ID 回 Redis 獲取完整數(shù)據(jù)(或搜索引擎已存儲(chǔ)所需數(shù)據(jù))。
- 優(yōu)點(diǎn): 提供極其強(qiáng)大和高效的全文搜索、復(fù)雜過濾、聚合分析能力;避免 Redis 自身遍歷。
- 缺點(diǎn): 系統(tǒng)架構(gòu)復(fù)雜度顯著增加;需要維護(hù)額外的服務(wù);數(shù)據(jù)同步有延遲(異步);運(yùn)維成本高。
?? 總結(jié)與建議
- 絕對禁止
KEYS: 總是用SCAN替代。 - 優(yōu)先考慮設(shè)計(jì)優(yōu)化: 這是最根本的解決方案。思考業(yè)務(wù)查詢需求,通過設(shè)計(jì)可查詢的鍵結(jié)構(gòu)(索引集合、Sorted Set、Hash 索引)將運(yùn)行時(shí)模糊匹配轉(zhuǎn)化為精確查找或小范圍掃描。 這是“空間換時(shí)間”的經(jīng)典應(yīng)用。
- 合理使用
SCAN:- 對于無法避免掃描的場景,務(wù)必用
SCAN。 - 適當(dāng)調(diào)整
COUNT值(如 500-1000),在單次耗時(shí)和總網(wǎng)絡(luò)往返次數(shù)之間找到最佳平衡點(diǎn)。 - 在客戶端處理好游標(biāo)迭代。
- 對于無法避免掃描的場景,務(wù)必用
- 考慮緩存: 對結(jié)果變化慢、查詢頻繁的模式,緩存
SCAN結(jié)果。 - 評估外部索引: 當(dāng)數(shù)據(jù)量大、查詢模式復(fù)雜、性能要求極高時(shí),認(rèn)真考慮引入 RediSearch 或 Elasticsearch 等專用搜索引擎。它們是為這類場景量身定制的。
關(guān)鍵權(quán)衡:
- 內(nèi)存 vs CPU/延遲: 優(yōu)化設(shè)計(jì)(索引)會(huì)消耗更多內(nèi)存,但極大降低查詢延遲和 CPU 消耗(避免了遍歷)。
- 寫入復(fù)雜度 vs 讀取復(fù)雜度: 維護(hù)索引增加了寫入/更新操作的復(fù)雜度(需要同時(shí)更新索引),但極大簡化并加速了讀取操作。
- 實(shí)時(shí)性 vs 效率: 外部索引通常是異步更新,犧牲了一點(diǎn)實(shí)時(shí)性換取了強(qiáng)大的查詢能力和可擴(kuò)展性。
選擇哪種策略取決于:
- 你的數(shù)據(jù)規(guī)模
- 查詢模式的復(fù)雜度和頻率
- 對查詢延遲的要求
- 對數(shù)據(jù)實(shí)時(shí)性的要求
- 可接受的內(nèi)存開銷
- 系統(tǒng)的復(fù)雜度容忍度
務(wù)必根據(jù)你的具體應(yīng)用場景進(jìn)行設(shè)計(jì)和選擇! 沒有放之四海而皆準(zhǔn)的最優(yōu)解,但遵循“避免運(yùn)行時(shí)掃描”的核心原則是關(guān)鍵。????
到此這篇關(guān)于redis中數(shù)據(jù)模糊查詢scan用法詳解的文章就介紹到這了,更多相關(guān)redis scan模糊查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用寶塔在服務(wù)器上配置Redis的詳細(xì)圖文教程
這篇文章主要給大家介紹了關(guān)于使用寶塔在服務(wù)器上配置Redis的相關(guān)資料,包括下載和安裝Redis,開放端口,修改配置文件以允許遠(yuǎn)程訪問和設(shè)置密碼,該過程對于理解Redis在項(xiàng)目部署中的配置提供了實(shí)用指導(dǎo),需要的朋友可以參考下2024-11-11
Redis數(shù)據(jù)結(jié)構(gòu)SortedSet的底層原理解析
這篇文章主要介紹了Redis數(shù)據(jù)結(jié)構(gòu)SortedSet的底層原理解析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Linux環(huán)境下升級redis的詳細(xì)步驟記錄
這篇文章主要給大家介紹了關(guān)于Linux環(huán)境下升級redis的詳細(xì)步驟,描述了如何從舊版本升級到新版本Redis,包括備份舊數(shù)據(jù)、下載和安裝新版本、復(fù)制配置文件和數(shù)據(jù)、停止舊版本并啟動(dòng)新版本的過程,需要的朋友可以參考下2024-12-12
如何使用注解方式實(shí)現(xiàn)?Redis?分布式鎖
這篇文章主要介紹了如何使用注解方式實(shí)現(xiàn)Redis分布式鎖,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,教大家如何優(yōu)雅的使用Redis分布式鎖,感興趣的小伙伴可以參考一下2022-07-07
基于session?Redis實(shí)現(xiàn)登錄
這篇文章主要介紹了基于session?Redis實(shí)現(xiàn)登錄的相關(guān)資料,需要的朋友可以參考下2023-10-10
使用Redis實(shí)現(xiàn)實(shí)時(shí)排行榜的示例
為了實(shí)現(xiàn)一個(gè)實(shí)時(shí)排行榜系統(tǒng),我們可以使用Redis的有序集合,本文主要介紹了使用Redis實(shí)現(xiàn)實(shí)時(shí)排行榜的示例,具有一定的參考價(jià)值,感興趣的可以了解一下2025-04-04
Redis使用RedisTemplate導(dǎo)致key亂碼問題解決
本文主要介紹了Redis使用RedisTemplate導(dǎo)致key亂碼問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06

