使用scan优化redisTemplate.keys

427 阅读1分钟

原实现

public Collection<Session> getActiveSessions() {
    Set<Object> keys = redisTemplate.keys(SESSION_PREFIX + "*");
    if (keys != null) {
        return keys.stream().map(o -> (Session) redisTemplate.opsForValue().get(o)).collect(Collectors.toSet());
    } else {
        return null;
    }
}

问题

redisTemplate.keys() 方法可能会有以下问题:

  1. 性能问题keys() 方法会遍历整个 Redis 数据库来匹配指定的模式,如果 Redis 数据库非常大,这可能会导致性能问题,因为它会阻塞 Redis 服务器。
  2. 潜在的阻塞:在生产环境中使用 keys() 方法可能会导致 Redis 服务器阻塞,因为它在执行期间会阻止其他命令的执行。
  3. 不推荐使用:Redis 官方并不推荐在生产环境中使用 keys() 方法,因为它可能会对性能造成影响。
  4. 不适合大规模数据:当 Redis 数据库中的键非常多时,keys() 方法的执行时间会随着键的数量呈线性增长,因此不适合大规模的数据集。

优化后

public Collection<Session> getActiveSessions() {
    String pattern = SESSION_PREFIX + "*";
    Set<String> keys =scan(pattern, SCAN_COUNT);
    if (CollectionUtils.isNotEmpty(keys)) {
        return keys.stream().map(o -> (Session) redisTemplate.opsForValue().get(o)).collect(Collectors.toSet());
    }
    return null;
}

public Set<String> scan(String pattern, Long count) {
    return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
        Set<String> keys = new HashSet<>();
        try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(pattern).count(count).build())) {
            while (cursor.hasNext()) {
                keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return keys;
    });
}