【问题标题】:Performance of token range based queries on partition keys?基于令牌范围的分区键查询的性能?
【发布时间】:2019-06-03 21:19:21
【问题描述】:

我正在根据我的分区键的令牌范围从 cassandra 节点中选择所有记录。

下面是代码:

public static synchronized List<Object[]> getTokenRanges(
      final Session session) {

    if (cluster == null) {
      cluster = session.getCluster();
    }

    Metadata metadata = cluster.getMetadata();

    return unwrapTokenRanges(metadata.getTokenRanges());
  }

  private static List<Object[]> unwrapTokenRanges(Set<TokenRange> wrappedRanges) {

    final int tokensSize = 2;
    List<Object[]> tokenRanges = new ArrayList<>();
    for (TokenRange tokenRange : wrappedRanges) {
      List<TokenRange> unwrappedTokenRangeList = tokenRange.unwrap();
      for (TokenRange unwrappedTokenRange : unwrappedTokenRangeList) {
        Object[] objects = new Object[tokensSize];
        objects[0] = unwrappedTokenRange.getStart().getValue();
        objects[1] = unwrappedTokenRange.getEnd().getValue();
        tokenRanges.add(objects);
      }
    }
    return tokenRanges;
  }

getTokenRanges 为我提供了所有节点上的 vnode 的所有令牌范围。

然后我使用这些令牌范围来查询 cassandra。 object[0] 持有 vnode 的开始令牌和 object[1] 结束令牌。

生成以下查询:

SELECT * FROM my_key_space.tablename WHERE token(id)><start token number> AND token(id)<= <end token number>;

在上面的id 列是分区键。

在 Cassandra 中不建议执行范围查询,那么,这个查询会是高效的吗?

据我所知,这个查询只会调用单个分区/vnode,不会调用多个分区,因此不应该有任何性能问题?这是正确的吗?

Cassandra 版本:3.x

【问题讨论】:

    标签: cassandra datastax-enterprise cassandra-3.0


    【解决方案1】:

    对令牌范围的查询是高效的,Spark 使用它们来有效地获取数据。但是您需要记住以下 - getTokenRanges 将为您提供所有现有的令牌范围,但有一些边缘情况 - 最后一个范围将从某个正数到代表第一个范围的负数,因此,您的查询不会做任何事情。基本上你会错过MIN_TOKEN 和第一个令牌之间以及最后一个令牌和MAX_TOKEN 之间的数据。 Spark 连接器generates different CQL statements 基于令牌。另外,您需要将查询路由到正确的节点 - 这可以通过 setRoutingToken 完成。

    类似的方法可以用于 Java 代码 (full code):

        Metadata metadata = cluster.getMetadata();
        Metadata metadata = cluster.getMetadata();
        List<TokenRange> ranges = new ArrayList(metadata.getTokenRanges());
        Collections.sort(ranges);
        System.out.println("Processing " + (ranges.size()+1) + " token ranges...");
    
        Token minToken = ranges.get(0).getStart();
        String baseQuery = "SELECT id, col1 FROM test.range_scan WHERE ";
        Map<String, Token> queries = new HashMap<>();
        // generate queries for every range
        for (int i = 0; i < ranges.size(); i++) {
            TokenRange range = ranges.get(i);
            Token rangeStart = range.getStart();
            Token rangeEnd = range.getEnd();
            if (i == 0) {
                queries.put(baseQuery + "token(id) <= " + minToken, minToken);
                queries.put(baseQuery + "token(id) > " + rangeStart + " AND token(id) <= " + rangeEnd, rangeEnd);
            } else if (rangeEnd.equals(minToken)) {
                queries.put(baseQuery + "token(id) > " + rangeStart, rangeEnd);
            } else {
                queries.put(baseQuery + "token(id) > " + rangeStart + " AND token(id) <= " + rangeEnd, rangeEnd);
            }
        }
    
        // Note: It could be speedup by using async queries, but for illustration it's ok
        long rowCount = 0;
        for (Map.Entry<String, Token> entry: queries.entrySet()) {
            SimpleStatement statement = new SimpleStatement(entry.getKey());
            statement.setRoutingToken(entry.getValue());
            ResultSet rs = session.execute(statement);
            // .... process data
       }
    

    【讨论】:

    • 感谢您对边缘案例的详细回答。在我的代码中,我正在做tokenRange.unwrap()。我在想这个调用会将令牌分成两部分:第一部分将是 MIN_TOKEN 的最后一个令牌,第二部分将是第一个令牌的 MIN_TOKEN。这不会解决你谈到的边缘情况吗?所以在我的情况下,如果我在节点上总共有 64 个令牌,我将获得包含 65 个条目的令牌列表,最后 2 个被解包。你能确认一下吗?
    • 在您对statement.setRoutingToken() 发表评论时,我在我正在运行的 Cassandra 版本中看不到此方法。我正在使用 spark-cassandra-connector_2.11,版本:2.3.2。有什么我可以使用的方法吗?此外,如果不传递路由令牌会发生什么,我假设此查询将由任何节点根据令牌范围路由到正确的节点?
    • 不,我检查过 - 对于令牌范围,此路由不会自动发生 - 这就是您需要明确传递它的原因。但是,如果您有 spark cassandra 连接器 - 为什么要手动操作?
    • 我正在使用 java 进行 spark 作业,不知道如何使用 spark cassandra 连接器创建 JavaRDD 令牌范围,而我的其他不是 spark 的应用程序也需要全选查询。
    • Spark 连接器会为您执行此操作 - 因此您无需关心它 - 只需查看我链接的源代码即可。此外,如果您使用 Spark,我建议使用 Spark SQL/Dataframes - 它有更多优化。您可以在此处找到更多示例:github.com/alexott/dse-java-playground/tree/master/src/main/…
    【解决方案2】:

    是的,token range 查询,与对实际分区键的普通范围查询相比,确实是高性能的,因为它们可以从磁盘顺序读取(分区按顺序令牌顺序存储在磁盘上) 并从同一个节点读取顺序数据(相邻令牌属于同一个节点)。

    Cassandra 提示您这种查询将执行良好,因为它不需要您使用“ALLOW FILTERING”。如果您尝试对实际的分区键(而不是其令牌)进行范围查询,这将需要您添加“允许过滤”以表明您知道这会导致性能下降。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-18
      • 1970-01-01
      • 2017-07-18
      • 2016-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多