使用springredis模板扫描命令

使用springredis模板扫描命令,spring,redis,spring-data,Spring,Redis,Spring Data,我正在尝试使用重新连接执行“扫描”命令。我不明白为什么下面的代码抛出NoTouchElementException RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection(); Cursor c = redisConnection.scan(scanOptions); while (c.hasNext()) { c.next(); } 例外情

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

RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
    Cursor c = redisConnection.scan(scanOptions);
    while (c.hasNext()) {
        c.next();
    }
例外情况:

位于的java.util.NoSuchElementException 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)


我使用的是spring数据redis 1.6.0-RELEASE和绝地2.7.2;我确实认为ScanCursor的实现在处理这个版本的案例时有点缺陷,但我没有检查以前的版本

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

一个“简单但危险”的方法将是有一个非常大的计数(例如100万或更多)。这将使REDIS不再试图搜索您庞大的密钥空间,以找到“至少或接近”您的大计数。别忘了-REDIS是单线程的,所以你刚刚牺牲了你的性能。用12米的REDIS键试试这个,你会发现尽管扫描可能会很高兴地返回非常高的计数值的结果,但在搜索期间它绝对不会做更多的事情

要解决您的问题,请执行以下操作:

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实现将正确地遵循扫描指令,并根据需要正确循环,以按照文档到达循环的末尾。我还没有找到一种在同一个光标内更改扫描选项的方法——因此,如果您在结果的一半得到一个NoTouchElementException,那么可能会有风险,您将重新开始(并且基本上会将部分工作做两次)


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

是的,我在1.6.6.RELEASE spring-data-redis.version中尝试过这一点。没有问题,下面简单的while循环代码就足够了。我将count值设置为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.
    }
我的旧代码

ScanOptions.scanOptions().match("*" + query + "*").count(10).build();
工作代码

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

为了让这个问题更清晰可见,您还应该标记“spring数据”、“扫描”以及其他可能的值。灵感来源于-一个简单的shell脚本,该脚本使用有效的光标循环调用redis cli,通过patternslatest spring data redis release检索键,解决了这个问题;请参阅Spring Data REDIS的ScanCursor实现是否将确保不会获得重复事件?因为在它的缺点部分中关于副本的声明是可以返回的。上面的代码有什么问题吗。与我的第一个代码片段一样,匹配查询构建了10条记录这样的小数据集。在这种情况下,游标将尝试查找下一个元素。使用Integer.MAX_值,结果集适用于所有类型的数据集。正如我在上面的回复()中提到的,REDIS是单线程的,所以您刚刚牺牲了性能。