1、背景
Redis中有一个经典的问题,在巨大的数据量的情况下,做类似于查找符合某种规则的Key的信息,这里就有两种方式:
- 一是keys命令,简单粗暴,由于Redis单线程这一特性,keys命令是以阻塞的方式执行的,keys是以遍历的方式实现的复杂度是 O(n),Redis库中的key越多,查找实现代价越大,产生的阻塞时间越长。
- 二是scan命令,以非阻塞的方式实现key值的查找,绝大多数情况下是可以替代keys命令的,可选性更强。
2、示例
2.1、scan命令
scan执行方法,执行结果是Cursor<Entry<HK, HV>>
public static Cursor<Map.Entry<Object, Object>> scan(String key) {
if (!StringUtils.isEmpty(key)) {
return redisTemplate.opsForHash().scan(key, ScanOptions.NONE);
}
return null;
}
Cursor游标每次会返回一个对象Map.Entry<Object, Object> entry = cursor.next(),entry 对象包含key和value,通过以下方式可以读取键值:
String key = String.valueOf(entry.getKey())
String.valueOf(entry.getValue())
debug截图如下,展示了每次游标获取的数据:
2.2、scan命令实例
比如一段遍历redis缓存的代码,不过该逻辑依据不同的场景存在优化空间。
@Override
public void traversalMessage() throws Exception {
Cursor<Map.Entry<Object,Object>> cursor = RedisCacheUtils.scan(CacheConsts.SOMETHING_KEY);
if (cursor == null) {
logger.info("通过scan(H key, ScanOptions options)方法获取匹配键值对记录为空");
return;
}
while (cursor.hasNext()) {
Map.Entry<Object, Object> entry = cursor.next();
String key = String.valueOf(entry.getKey());
Object value = entry.getValue();
}
}
待优化点:
- 若数据量小,但处理逻辑复杂,建议先缓存遍历数据,再对数据进行处理。
- 若数据量大,遍历时需要考虑范围条件,对遍历数据量进行有效控制。