一.从海量的key里面查询出某一固定前缀的key
问题:假如redis里面有一亿个key,其中有十万个key是以某一个固定的已知的前缀开头的,如何 将他们全部找出来。
留意细节:摸清数据规模,即问清除边界(一定要问清楚数据量);
1. 使用KEYS pattern:查找所有符合给定模式pattern的key。(使用dbsize命令,可以查看redis中的数据量。)

假如该redis正在给线上的业务提供服务。使用KEYS指令会有什么问题?
缺点:(1)KEYS 指令一次性返回所有的匹配的key
(2)键的数量过大会使服务卡顿(当redis中的key过多时候,对内存的消耗和redis服务器都是一个隐患)。
2. SCAN指令可以无阻塞的提取出指定模式的key列表,SCAN每次执行都只会返回少量元素,所以可以用于生产环境,而不会出现像KEYS命令带来的可能会阻塞服务器的问题。
SCAN cursor [MATCH pattern][COUNT count]
备注:
cursor指的是游标
[MATCH pattern]指的是要匹配的值
SCAN特点:
- 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程;
- 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历;
- 不保证每次执行都返回某个给定数量的元素,支持模糊查询;
- 一次返回的数量不可控,只能是大概率符合count参数。
使用count选项对命令的行为进行一定程度上的调整,count 选项的作用就是让用户告知迭代命令在每次迭代中应该从数据集里返回多少元素 ,使用count选项对于增量式迭代命令相当于是一种提示,大多数情况下这种提示都是比较有效的控制了返回的数量的。值得注意的是,使用count选项并不能严格控制返回的key数量,只能是个大致的约束,并非每次迭代都返回count数量的元素。用户可以在每次迭代中根据自己的需要,随意改变count值,只要记得将上次迭代返回的游标放到下次迭代就可以了。
如图所示:执行结果第一个值为当前的游标,第二个值为返回key的结果集。

执行该指令redis并不会卡顿,同时我们只返回了部分值。
可以把返回的当前游标继续作为参数传入进去。

返回的cursor游标不一定是递增的,可能后一次返回的游标比前一次还小。
也就是说很有可能获取到重复key的问题,所以需要在外部程序中做一下去重。
由于是分批次遍历的,所以SCAN整体花费的时间要比keys高。
二.代码查询key数据
1.拿连本地的客户端为例。先查询redis的端口号。
进入redis目录下,打开其redis.conf文件,查看其port号。
例:vim /usr/local/redis-5.0.4/redis.conf

2.使用maven导入redis相关包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.5.2</version>
</dependency>
3.操作代码如下:
(1)使用keys查询数据
/**
* 使用redis的keys* 命令获取返回结果
*
* @param args
*/
public static void main(String[] args) {
useKeys("t*");
}
/**
* 使用keys* 命令
* @param key 要匹配的值
*/
public static void useKeys(String key) {
// 创建连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 使用keys* 命令获得返回结果
Set<String> keysResult = jedis.keys(key);
System.out.println(keysResult);
jedis.close();
}
(2)使用scan查询数据
/**
* 使用redis的keys* 命令获取返回结果
*
* @param args
*/
public static void main(String[] args) {
scan("t*", 0,2);
}
/**
* 使用scan 命令
* @param key 要匹配的值
* @param beginData 初始游标的位置
* @param count 每次返回的大概数量
*/
public static void scan(String key, int beginData,int count) {
// 创建连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
int i = beginData;
// 由于可能有重复,所以用一个set接收去重
Set<String> scanResultSet = new HashSet<String>();
while (true) {
i = getReturnCursor(key, jedis, i, scanResultSet,count);
if (i == 0) {
break;
}
}
System.out.println(scanResultSet);
jedis.close();
}
/**
* 执行redis的scan操作并获得其返回游标值
* @param key 要匹配的值
* @param jedis jedis连接
* @param beginData 开始值
* @param scanResultSet 返回的结果集合
* @param count 每次返回的大概数量
* @return 游标位置
*/
public static int getReturnCursor(String key, Jedis jedis, int beginData,
Set<String> scanResultSet, int count) {
// 创建scanParam
ScanParams scanParams = new ScanParams();
// match的值,如我们要找t*开头的key值
scanParams.match(key);
// count的值,如返回我们约等于2个的数据
scanParams.count(count);
// 从第0个游标开始获取
ScanResult s = jedis.scan(beginData, scanParams);
scanResultSet.addAll(s.getResult());
return s.getCursor();
}
本文介绍了在Redis中如何从大量键中查询具有特定前缀的键,对比了使用KEYS和SCAN指令的优缺点。KEYS指令可能导致服务卡顿,不适合生产环境。而SCAN指令作为无阻塞的迭代器,可以更安全地用于生产环境,但需注意游标管理和可能的重复键问题。此外,文章还展示了通过代码使用SCAN查询Redis数据的步骤。
488

被折叠的 条评论
为什么被折叠?



