【问题标题】:SCAN command with spring redis template使用 spring redis 模板扫描命令
【发布时间】:2015-12-20 06:26:44
【问题描述】:

我正在尝试使用 RedisConnection 执行“扫描”命令。我不明白为什么下面的代码会抛出 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)

【问题讨论】:

  • 为了让这个问题更明显,您还应该使用“spring-data”、“scan”以及可能的其他值进行标记。

标签: spring redis spring-data


【解决方案1】:

是的,我在 1.6.6.RELEASE 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.
    }

【讨论】:

    【解决方案2】:

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

    所以:解释起来相当复杂,但在 ScanOptions 对象中有一个“计数”字段需要设置(默认为 10)。此字段包含此搜索的“意图”或“预期”结果。正如所解释的(不是很清楚,恕我直言)here,您可以在每次调用时更改 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();
      }
    }
    

    请注意,Spring Data REDIS 的 ScanCursor 实现将正确遵循 SCAN 指令并根据需要正确循环,以根据文档到达循环结束。我还没有找到一种方法来更改同一光标内的扫描选项 - 因此,如果您在结果中途获得了 NoSuchElementException,您可能会重新开始(并且基本上会做一些工作两次)。

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

    【讨论】:

    【解决方案3】:

    我的旧代码

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

    工作代码

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

    【讨论】:

    • 上面的代码有什么问题。与我的第一个代码 sn-p 一样,匹配查询构建了 10 条记录之类的小数据集。在这种情况下,光标尝试查找下一个元素。使用 Integer.MAX_VALUE 结果集适用于所有类型的数据集。
    • 正如我在上面的回复 (stackoverflow.com/a/33133756/1823881) 中提到的,REDIS 是单线程的,所以你只是扼杀了你的性能。
    猜你喜欢
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    • 2019-02-17
    相关资源
    最近更新 更多