【问题标题】:ZSCAN guarantee for elements which score has changed during iterationZSCAN 保证在迭代过程中分数发生变化的元素
【发布时间】:2016-09-20 02:03:02
【问题描述】:

我在文档中找不到此信息:Redis 是否保证在此条件下使用 ZSCAN 命令返回元素:

该元素从头到尾都包含在排序集中 一个完整的迭代,但这样的元素的分数已经改变(甚至 在迭代期间多次,例如由另一个客户端)?

我发现的唯一相关声明如下:

在某个时间段内不经常出现在集合中的元素 完整的迭代,可以返回也可以不返回:它是未定义的。

但我不知道这种情况下的分数变化是否与删除/添加操作相同。

【问题讨论】:

    标签: redis


    【解决方案1】:

    如果元素在整个迭代过程中存在,它将由zscan 命令返回。迭代过程中分数是否发生变化无关紧要。

    通常,zset 实现为哈希表(即 Redis 的 dict)和跳过列表。运行zscan 命令时,它会遍历哈希表条目以执行扫描作业。分数(dict entry的值)的变化不会影响迭代过程。

    如果zset 足够小,Redis 将其实现为ziplist。在这种情况下,Redis 在单个 zscan 调用中返回所有元素。所以在迭代过程中分数不能改变。

    总之,你有保证。

    【讨论】:

      【解决方案2】:

      非常感谢 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 命令的迭代期间发生了变化。

      【讨论】:

        猜你喜欢
        • 2021-09-28
        • 2015-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多