使用spring redis模板扫描命令



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

RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
    Cursor c = redisConnection.scan(scanOptions);
    while (c.hasNext()) {
        c.next();
    }
异常:

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)

是的,我已经尝试过这个,在1.6.6。释放spring-data-redis.version。没有问题,下面简单的while循环代码就足够了。并且我已经将计数值设置为100(更大的值)以节省往返时间。

    RedisConnection redisConnection = null;
    try {
        redisConnection = redisTemplate.getConnectionFactory().getConnection();
        ScanOptions options = ScanOptions.scanOptions().match(workQKey).count(100).build();
        Cursor c = redisConnection.scan(options);
        while (c.hasNext()) {
            logger.info(new String((byte[]) c.next()));
        }
    } finally {
        redisConnection.close(); //Ensure closing this connection.
    }

我使用spring-data-redis 1.6.0-RELEASE和Jedis 2.7.2;我确实认为ScanCursor的实现在这个版本上处理这种情况时有点缺陷——尽管我没有检查过以前的版本。

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

一个"简单但危险"的方法是有一个非常大的计数(例如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();
  }
}

请注意,Spring Data REDIS的ScanCursor实现将正确地遵循SCAN指令并正确循环,尽可能多地根据需要,按照文档到达循环的末尾。我还没有找到一种方法来改变同一光标内的扫描选项-所以可能有这样的风险,如果您得到一半的结果,并得到一个NoSuchElementException,您将重新开始(基本上做一些工作两次)。

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

我的旧代码

ScanOptions.scanOptions().match("*" + query + "*").count(10).build();

工作代码

ScanOptions.scanOptions().match("*" + query + "*").count(Integer.MAX_VALUE).build();

最新更新