带有 Spring Redis 模板的 SCAN 命令

coo*_*ist 5 spring redis spring-data

我正在尝试使用 RedisConnection 执行“扫描”命令。我不明白为什么下面的代码抛出 NoSuchElementException

RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
    Cursor c = redisConnection.scan(scanOptions);
    while (c.hasNext()) {
        c.next();
    }
Run Code Online (Sandbox Code Playgroud)

例外:

java.util.NoSuchElementException 在 java.util.Collections$EmptyIterator.next(Collections.java:4189) 在 org.springframework.data.redis.core.ScanCursor.moveNext(ScanCursor.java:215) 在 org.springframework.data。 redis.core.ScanCursor.next(ScanCursor.java:202)

小智 3

我正在使用 spring-data-redis 1.6.0-RELEASE 和 Jedis 2.7.2;我确实认为 ScanCursor 实现在处理此版本上的这种情况时使用 rgds 略有缺陷 - 但我没有检查以前的版本。

所以:解释起来相当复杂,但在 ScanOptions 对象中有一个“计数”字段需要设置(默认为 10)。该字段包含此搜索的“意图”或“预期”结果。正如此处所解释的(恕我直言,不是很清楚),您可以在每次调用时更改 count 的值,特别是在没有返回结果的情况下。我将其理解为“工作意图”,因此如果您没有得到任何回报,也许您的“密钥空间”很大并且 SCAN 命令没有“足够努力”工作。显然,只要您能得到结果,就不需要增加此值。

一种“简单但危险”的方法是拥有非常大的计数(例如 100 万或更多)。这将使 REDIS 不再尝试搜索您庞大的密钥空间以找到“至少或接近”与您的大计数一样多的内容。不要忘记 - REDIS是单线程的,所以你只会扼杀你的性能。使用具有 12M 键的 REDIS 尝试此操作,您会发现虽然 SCAN 可能会很高兴地返回具有非常高计数值的结果,但在搜索期间它绝对不会执行任何其他操作。

对于您的问题的解决方案:

ScanOptions options = ScanOptions.scanOptions().match(pattern).count(countValue).build();
boolean done = false;

// the while-loop below makes sure that we'll get a valid cursor - 
// by looking harder if we don't get a result initially
while (!done) {
  try(Cursor c = redisConnection.scan(scanOptions)) {
    while (c.hasNext()) {
       c.next();
    }
    done = true; //we've made it here, lets go away
  } catch (NoSuchElementException nse) {
    System.out.println("Going for "+countValue+" was not hard enough. Trying harder");
    options = ScanOptions.scanOptions().match(pattern).count(countValue*2).build();
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,Spring Data REDIS 的 ScanCursor 实现将正确遵循 SCAN 指令并根据需要正确循环,以按照文档到达循环末尾。我还没有找到一种方法来更改同一光标内的扫描选项 - 因此可能存在这样的风险:如果您完成结果的一半并收到 NoSuchElementException,您将重新开始(并且本质上会执行一些操作)工作两次)。

当然,更好的解决方案总是受欢迎的:)