Spring Redis 模糊查询匹配key
背景
原方法在Redis内存储条数过多时效率较低,尝试进行改良。修改模糊查询方式后有效提高查询效率。
改良步骤
原方法
原模糊查询方法采用ScanOptions匹配key的前缀,但并未设置游标步进值,导致其实际效率可能跟使用keys命令相同。
改进
- 改用lettuce api RedisAsynCommands构建Redis连接。
- 加入游标步进值实现迭代查询。
- 由于scan命令可能得到重复的key,使用set进行去重。
参考代码
/**
* 获取 指定格式的所有key
* 迭代执行 SCAN 0 MATCH {pattern} COUNT 10000
*
* @param redisTemplate redisTemplate
* @param pattern 匹配规则
* @return 指定格式的所有key
*/
public static List<String> scanKeys(RedisTemplate redisTemplate, String pattern) {
//SCAN 0 MATCH {pattern} COUNT 10000
return (List<String>) redisTemplate.execute(connection -> {
//scan 迭代遍历键,返回的结果可能会有重复,需要客户端去重复
Set<String> redisKeys = new HashSet<>();
//lettuce 原生api
RedisAsyncCommands conn = (RedisAsyncCommands) connection.getNativeConnection();
//游标
ScanCursor curs = ScanCursor.INITIAL;
try {
//采用 SCAN 命令,迭代遍历所有key
while (!curs.isFinished()) {
long count = 10000L;
ScanArgs args = ScanArgs.Builder.matches(pattern).limit(count);
log.info("SCAN {} MATCH {} COUNT {}", curs.getCursor(), pattern, count);
RedisFuture<KeyScanCursor<byte[]>> future = conn.scan(curs, args);
KeyScanCursor<byte[]> keyCurs = future.get();
List<byte[]> ks = keyCurs.getKeys();
Set<String> set = ks.stream().map(bytes -> new String(bytes, StandardCharsets.UTF_8)).collect(Collectors.toSet());
log.info("return size:{}", set.size());
redisKeys.addAll(set);
curs = keyCurs;
}
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
return new ArrayList<>(redisKeys);
}, true);
}