问题:假如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特点:
使用count选项对命令的行为进行一定程度上的调整,count 选项的作用就是让用户告知迭代命令在每次迭代中应该从数据集里返回多少元素 ,使用count选项对于增量式迭代命令相当于是一种提示,大多数情况下这种提示都是比较有效的控制了返回的数量的。值得注意的是,使用count选项并不能严格控制返回的key数量,只能是个大致的约束,并非每次迭代都返回count数量的元素。用户可以在每次迭代中根据自己的需要,随意改变count值,只要记得将上次迭代返回的游标放到下次迭代就可以了。
如图所示:执行结果第一个值为当前的游标,第二个值为返回key的结果集。
执行该指令redis并不会卡顿,同时我们只返回了部分值。
可以把返回的当前游标继续作为参数传入进去。
返回的cursor游标不一定是递增的,可能后一次返回的游标比前一次还小。
也就是说很有可能获取到重复key的问题,所以需要在外部程序中做一下去重。
由于是分批次遍历的,所以SCAN整体花费的时间要比keys高。
1.拿连本地的客户端为例。先查询redis的端口号。
进入redis目录下,打开其redis.conf文件,查看其port号。
例:vim /usr/local/redis-5.0.4/redis.conf
2.使用maven导入redis相关包
redis.clients
jedis
2.5.2
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 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 scanResultSet = new HashSet();
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 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();
}