Redis高效查詢大數(shù)據(jù)的實(shí)踐與優(yōu)化詳細(xì)指南
1. 引言
Redis 是一種高性能的鍵值存儲(chǔ)數(shù)據(jù)庫,廣泛應(yīng)用于緩存、排行榜、計(jì)數(shù)器等場景。在實(shí)際業(yè)務(wù)中,我們經(jīng)常需要查詢符合特定條件的數(shù)據(jù),例如找出 value 大于某個(gè)閾值(如 10)的鍵值對(duì)。然而,直接遍歷所有鍵并使用 GET 命令逐個(gè)檢查可能會(huì)導(dǎo)致性能問題,尤其是當(dāng)數(shù)據(jù)量較大時(shí)。
本文將圍繞 如何高效查詢 Redis 中滿足條件的數(shù)據(jù) 展開討論,從最初的簡單實(shí)現(xiàn)到優(yōu)化后的高效方案,并結(jié)合 Java 代碼示例,幫助開發(fā)者掌握 Redis 數(shù)據(jù)查詢的最佳實(shí)踐。
2. 問題背景
假設(shè)我們有以下需求:
Redis 數(shù)據(jù)庫 DB1(-n 1)存儲(chǔ)了大量形如 flow:count:1743061930:* 的鍵。
需要找出其中 value > 10 的所有鍵值對(duì),并統(tǒng)計(jì)總數(shù)。
初始實(shí)現(xiàn)方案
最初的 Shell 腳本如下:
redis-cli -h 10.206.0.16 -p 6379 -n 1 --scan --pattern "flow:count:1743061930:*" | \
while read key; do
value=$(redis-cli -h 10.206.0.16 -p 6379 -n 1 GET "$key")
if [ "$value" != "1" ]; then
echo "$key: $value"
fi
done | tee /dev/stderr | wc -l | awk '{print "Total count: " $1}'
該方案的問題:
多次 Redis 查詢:每個(gè)鍵都要單獨(dú)執(zhí)行 GET,網(wǎng)絡(luò)開銷大。
Shell 字符串比較低效:[ "$value" != "1" ] 是字符串比較,數(shù)值比較更合適。
管道過多:tee、wc、awk 多個(gè)管道影響性能。
3. 優(yōu)化方案
3.1 優(yōu)化 Shell 腳本
優(yōu)化后的版本:
redis-cli -h 10.206.0.16 -p 6379 -n 1 --scan --pattern "flow:count:1743061930:*" | \
while read key; do
redis-cli -h 10.206.0.16 -p 6379 -n 1 GET "$key"
done | \
awk '$1 > 10 {count++; print} END {print "Total count: " count}'
優(yōu)化點(diǎn):
- 減少 Redis 命令調(diào)用:直接批量獲取 value,減少網(wǎng)絡(luò)開銷。
- 使用 awk 進(jìn)行數(shù)值比較:$1 > 10 比 Shell 字符串比較更高效。
- 合并計(jì)數(shù)邏輯:awk 同時(shí)完成過濾、輸出和計(jì)數(shù)。
如果仍需保留鍵名:
redis-cli -h 10.206.0.16 -p 6379 -n 1 --scan --pattern "flow:count:1743061930:*" | \
while read key; do
value=$(redis-cli -h 10.206.0.16 -p 6379 -n 1 GET "$key")
echo "$key: $value"
done | \
awk -F': ' '$2 > 10 {count++; print} END {print "Total count: " count}'
3.2 使用 Redis Pipeline 優(yōu)化
Shell 腳本仍然存在多次 GET 的問題,我們可以使用 Redis Pipeline 批量獲取數(shù)據(jù),減少網(wǎng)絡(luò)往返時(shí)間。
優(yōu)化后的 Shell + Pipeline 方案
redis-cli -h 10.206.0.16 -p 6379 -n 1 --scan --pattern "flow:count:1743061930:*" | \
xargs -I {} redis-cli -h 10.206.0.16 -p 6379 -n 1 MGET {} | \
awk '$1 > 10 {count++; print} END {print "Total count: " count}'
這里使用 xargs + MGET 批量獲取 value,減少網(wǎng)絡(luò)請(qǐng)求次數(shù)。
4. Java 實(shí)現(xiàn)方案
在 Java 應(yīng)用中,我們可以使用 Jedis 或 Lettuce 客戶端優(yōu)化查詢。
4.1 使用 Jedis 查詢
import redis.clients.jedis.Jedis; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; import java.util.List; public class RedisValueFilter { public static void main(String[] args) { String host = "10.206.0.16"; int port = 6379; int db = 1; String pattern = "flow:count:1743061930:*"; int threshold = 10; try (Jedis jedis = new Jedis(host, port)) { jedis.select(db); ScanParams scanParams = new ScanParams().match(pattern).count(100); String cursor = "0"; int totalCount = 0; do { ScanResult<String> scanResult = jedis.scan(cursor, scanParams); List<String> keys = scanResult.getResult(); cursor = scanResult.getCursor(); // 批量獲取 values List<String> values = jedis.mget(keys.toArray(new String[0])); // 過濾并統(tǒng)計(jì) for (int i = 0; i < keys.size(); i++) { String key = keys.get(i); String valueStr = values.get(i); if (valueStr != null) { int value = Integer.parseInt(valueStr); if (value > threshold) { System.out.println(key + ": " + value); totalCount++; } } } } while (!cursor.equals("0")); System.out.println("Total count: " + totalCount); } } }
優(yōu)化點(diǎn):
- 使用 SCAN 代替 KEYS,避免阻塞 Redis。
- 使用 MGET 批量查詢,減少網(wǎng)絡(luò)開銷。
- 直接數(shù)值比較,提高效率。
4.2 使用 Lettuce(異步非阻塞)
Lettuce 是高性能 Redis 客戶端,支持異步查詢:
import io.lettuce.core.*; import io.lettuce.core.api.sync.RedisCommands; import java.util.List; public class RedisLettuceQuery { public static void main(String[] args) { RedisURI uri = RedisURI.create("redis://10.206.0.16:6379/1"); RedisClient client = RedisClient.create(uri); try (RedisConnection<String, String> connection = client.connect()) { RedisCommands<String, String> commands = connection.sync(); String pattern = "flow:count:1743061930:*"; int threshold = 10; int totalCount = 0; ScanCursor cursor = ScanCursor.INITIAL; do { ScanArgs scanArgs = ScanArgs.Builder.matches(pattern).limit(100); KeyScanCursor<String> scanResult = commands.scan(cursor, scanArgs); List<String> keys = scanResult.getKeys(); cursor = ScanCursor.of(scanResult.getCursor()); // 批量獲取 values List<KeyValue<String, String>> keyValues = commands.mget(keys.toArray(new String[0])); for (KeyValue<String, String> kv : keyValues) { if (kv.hasValue()) { int value = Integer.parseInt(kv.getValue()); if (value > threshold) { System.out.println(kv.getKey() + ": " + value); totalCount++; } } } } while (!cursor.isFinished()); System.out.println("Total count: " + totalCount); } finally { client.shutdown(); } } }
優(yōu)勢(shì):
非阻塞 I/O,適合高并發(fā)場景。
支持 Reactive 編程(如 RedisReactiveCommands)。
5. 性能對(duì)比
方案 | 查詢方式 | 網(wǎng)絡(luò)開銷 | 適用場景 |
---|---|---|---|
原始 Shell | 單 GET 遍歷 | 高 | 少量數(shù)據(jù) |
優(yōu)化 Shell + awk | 批量 GET | 中 | 中等數(shù)據(jù)量 |
Shell + Pipeline | MGET 批量 | 低 | 大數(shù)據(jù)量 |
Java + Jedis | SCAN + MGET | 低 | 生產(chǎn)環(huán)境 |
Java + Lettuce | 異步 SCAN | 最低 | 高并發(fā) |
6. 結(jié)論
- 避免 KEYS 命令:使用 SCAN 替代,防止阻塞 Redis。
- 減少網(wǎng)絡(luò)請(qǐng)求:使用 MGET 或 Pipeline 批量查詢。
- 數(shù)值比較優(yōu)化:用 awk 或 Java 直接比較數(shù)值,而非字符串。
- 生產(chǎn)推薦:Java + Jedis/Lettuce 方案,適合大規(guī)模數(shù)據(jù)查詢。
通過優(yōu)化,我們可以顯著提升 Redis 大數(shù)據(jù)查詢的效率,降低服務(wù)器負(fù)載,適用于高并發(fā)生產(chǎn)環(huán)境。
到此這篇關(guān)于Redis高效查詢大數(shù)據(jù)的實(shí)踐與優(yōu)化詳細(xì)指南的文章就介紹到這了,更多相關(guān)Redis查詢數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
redis key命名規(guī)范的設(shè)計(jì)
如果結(jié)構(gòu)規(guī)劃不合理、命令使用不規(guī)范,會(huì)造成系統(tǒng)性能達(dá)到瓶頸、活動(dòng)高峰系統(tǒng)可用性下降,也會(huì)增大運(yùn)維難度,本文主要介紹了redis key命名規(guī)范的設(shè)計(jì),感興趣的可以了解一下2024-03-03Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實(shí)現(xiàn)方法
這篇文章主要介紹了Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實(shí)現(xiàn)方法,本篇文章所述的dict在Redis中最主要的作用就是用于維護(hù)Redis數(shù)據(jù)庫中所有Key、value映射的數(shù)據(jù)結(jié)構(gòu),需要的朋友可以參考下2022-05-05Redis 哨兵機(jī)制及配置實(shí)現(xiàn)
本文主要介紹了Redis 哨兵機(jī)制及配置實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03redis+lua實(shí)現(xiàn)限流的項(xiàng)目實(shí)踐
redis有很多限流的算法(比如:令牌桶,計(jì)數(shù)器,時(shí)間窗口)等,在分布式里面進(jìn)行限流的話,我們則可以使用redis+lua腳本進(jìn)行限流,下面就來介紹一下redis+lua實(shí)現(xiàn)限流2023-10-10redis?sentinel監(jiān)控高可用集群實(shí)現(xiàn)的配置步驟
這篇文章主要介紹了redis?sentinel監(jiān)控高可用集群實(shí)現(xiàn)的配置步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04redis集群搭建_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了redis集群搭建,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08Redis如何實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離詳解
Redis的主從架構(gòu),能幫助我們實(shí)現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關(guān)于Redis如何實(shí)現(xiàn)數(shù)據(jù)庫讀寫分離的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03