我正在尝试执行"扫描"使用RedisConnection命令。我不明白为什么以下代码抛出NoSuchElementException
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
Cursor c = redisConnection.scan(scanOptions);
while (c.hasNext()) {
c.next();
}
例外:
java.util.NoSuchElementException at java.util.Collections $ EmptyIterator.next(Collections.java:4189)at org.springframework.data.redis.core.ScanCursor.moveNext(ScanCursor.java:215) 在 org.springframework.data.redis.core.ScanCursor.next(ScanCursor.java:202)
答案 0 :(得分:2)
我正在使用spring-data-redis 1.6.0-RELEASE和Jedis 2.7.2;我确实认为ScanCursor实现在这个版本上处理这种情况时会有轻微的缺陷 - 我还没有检查过以前的版本。
所以:解释相当复杂,但在ScanOptions对象中有一个需要设置的“count”字段(默认为10)。此字段包含此搜索的“意图”或“预期”结果。正如所解释的(不是很清楚,恕我直言)here,您可以在每次调用时更改count的值,尤其是在没有返回结果的情况下。我理解这是“工作意图”所以如果你没有得到任何回报,也许你的“关键空间”是巨大的,并且SCAN命令没有“足够努力”。显然,只要你得到结果,你就不需要增加它。
“简单但危险”的方法是拥有非常大的数量(例如100万或更多)。这将使REDIS消失,试图搜索你的巨大密钥空间,以找到“至少或接近尽可能多”的数量。不要忘记 - REDIS 是单线程所以你刚刚杀死了你的表现。尝试使用REDIS的12M键,你会发现尽管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,你将重新开始(并且基本上会做一些工作两次)。
当然,总是欢迎更好的解决方案:)
答案 1 :(得分:0)
是的,我在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 :(得分:-1)
我的旧代码
ScanOptions.scanOptions().match("*" + query + "*").count(10).build();
工作代码
ScanOptions.scanOptions().match("*" + query + "*").count(Integer.MAX_VALUE).build();