1. 引言
redis 是一种高性能的键值存储数据库,广泛应用于缓存、排行榜、计数器等场景。在实际业务中,我们经常需要查询符合特定条件的数据,例如找出 value 大于某个阈值(如 10)的键值对。然而,直接遍历所有键并使用 get 命令逐个检查可能会导致性能问题,尤其是当数据量较大时。
本文将围绕 如何高效查询 redis 中满足条件的数据 展开讨论,从最初的简单实现到优化后的高效方案,并结合 java 代码示例,帮助开发者掌握 redis 数据查询的最佳实践。
2. 问题背景
假设我们有以下需求:
redis 数据库 db1(-n 1)存储了大量形如 flow:count:1743061930:* 的键。
需要找出其中 value > 10 的所有键值对,并统计总数。
初始实现方案
最初的 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 查询:每个键都要单独执行 get,网络开销大。
shell 字符串比较低效:[ "$value" != "1" ] 是字符串比较,数值比较更合适。
管道过多:tee、wc、awk 多个管道影响性能。
3. 优化方案
3.1 优化 shell 脚本
优化后的版本:
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}'
优化点:
- 减少 redis 命令调用:直接批量获取 value,减少网络开销。
- 使用 awk 进行数值比较:$1 > 10 比 shell 字符串比较更高效。
- 合并计数逻辑:awk 同时完成过滤、输出和计数。
如果仍需保留键名:
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 优化
shell 脚本仍然存在多次 get 的问题,我们可以使用 redis pipeline 批量获取数据,减少网络往返时间。
优化后的 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,减少网络请求次数。
4. java 实现方案
在 java 应用中,我们可以使用 jedis 或 lettuce 客户端优化查询。
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])); // 过滤并统计 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); } } }
优化点:
- 使用 scan 代替 keys,避免阻塞 redis。
- 使用 mget 批量查询,减少网络开销。
- 直接数值比较,提高效率。
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(); } } }
优势:
非阻塞 i/o,适合高并发场景。
支持 reactive 编程(如 redisreactivecommands)。
5. 性能对比
方案 | 查询方式 | 网络开销 | 适用场景 |
---|---|---|---|
原始 shell | 单 get 遍历 | 高 | 少量数据 |
优化 shell + awk | 批量 get | 中 | 中等数据量 |
shell + pipeline | mget 批量 | 低 | 大数据量 |
java + jedis | scan + mget | 低 | 生产环境 |
java + lettuce | 异步 scan | 最低 | 高并发 |
6. 结论
- 避免 keys 命令:使用 scan 替代,防止阻塞 redis。
- 减少网络请求:使用 mget 或 pipeline 批量查询。
- 数值比较优化:用 awk 或 java 直接比较数值,而非字符串。
- 生产推荐:java + jedis/lettuce 方案,适合大规模数据查询。
通过优化,我们可以显著提升 redis 大数据查询的效率,降低服务器负载,适用于高并发生产环境。
到此这篇关于redis高效查询大数据的实践与优化详细指南的文章就介绍到这了,更多相关redis查询数据内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论