我在文档中找不到这些信息:Redis是否保证在这种情况下使用ZSCAN命令返回一个元素:
元素从开头到结尾包含在有序集中 一个完整的迭代,但这个元素的分数已经改变(甚至 迭代期间,例如由另一个客户端多次?
我发现的相关陈述如下:
在a期间不会经常出现在集合中的元素 完整的迭代,可以返回或不返回:它是未定义的。
但我不知道在这种情况下分数变化是否与删除/添加操作相同。
答案 0 :(得分:1)
如果元素在完整迭代期间存在,则zscan
命令将返回该元素。在迭代过程中得分是否已经改变并不重要。
通常,zset
实现为哈希表(即Redis' dict
)和跳转列表。运行zscan
命令时,它会遍历哈希表条目以执行扫描作业。分数的变化(字典条目的值)不会影响迭代过程。
如果zset
足够小,Redis会将其实现为ziplist
。在这种情况下,Redis会在单个zscan
调用中返回所有元素。因此,在迭代过程中不能改变分数。
总之,您有保证。
答案 1 :(得分:0)
非常感谢 for_stack 进行确认。我不知道是否会有人回复,同时我在Java中实现了一些自己的检查:
@Test
public void testZScanGuaranteeWithScoreUpdates() {
try (Jedis jedis = jedisPool.getResource()) {
IntStream.rangeClosed(1, 50).forEach(i -> testZScanGuaranteeWithUpdates(jedis, false));
IntStream.rangeClosed(1, 50).forEach(i -> testZScanGuaranteeWithUpdates(jedis, true));
}
}
/**
* Changing score of elements (named ids) during iteration (eventually inserting and removing another unchecked ids)
* and then assert that no element (id) is missing
*/
private void testZScanGuaranteeWithUpdates(Jedis jedis, boolean noise) {
Random ran = new Random();
List<String> noiseIds = IntStream.range(0, 4000).mapToObj(i -> UUID.randomUUID().toString()).collect(toList());
if (noise) { // insert some noise with random score (from 0 to 5000)
noiseIds.forEach(id -> jedis.zadd(KEY, ran.nextInt(5000), id));
}
int totalIds = 2000;
List<String> ids = IntStream.range(0, totalIds).mapToObj(i -> UUID.randomUUID().toString()).collect(toList());
Set<String> allScanned = new HashSet<>();
ids.forEach(id -> jedis.zadd(KEY, ran.nextInt(2500) + 1000, id)); // insert all IDs with random score (from 1000 to 3500)
redis.scanIds(KEY, 100, res -> { // encapsulate iteration step - this closure is executed for every 100 elements during iteration
allScanned.addAll(res); // record 100 scanned ids
Collections.shuffle(ids);
ids.stream().limit(500).forEach(id -> jedis.zadd(KEY, ran.nextInt(2500) + 1000, id)); // change score of 500 random ids
if (noise) { // insert and remove some noise
IntStream.range(0, 50).forEach(i -> jedis.zadd(KEY, ran.nextInt(5000), UUID.randomUUID().toString()));
IntStream.range(0, 60).forEach(i -> jedis.zrem(KEY, noiseIds.get(ran.nextInt(noiseIds.size()))));
}
});
if (!noise) {
assertEquals(totalIds, allScanned.size()); // 2000 unique ids scanned
}
assertTrue(allScanned.containsAll(ids)); // none id is missing
jedis.del(KEY); // prepare for test re-execution
}
测试正在通过,即ZSCAN返回所有元素,即使在使用ZADD命令迭代期间其分数发生了变化。