在分区键上基于令牌范围的查询的性能?

时间:2019-01-09 06:31:56

标签: cassandra datastax-enterprise cassandra-3.0

我正在根据分区键的令牌范围从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

2 个答案:

答案 0 :(得分:1)

与实际分区键上的普通范围查询相比,

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

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

答案 1 :(得分:1)

关于令牌范围的查询是高效的,Spark使用它们进行有效的数据提取。但是您需要记住以下几点-getTokenRanges将为您提供所有现有的令牌范围,但是有一些边缘情况-最后一个范围是从某个正数到负数,它们代表第一个范围,并且这样,您的查询将不会执行任何操作。基本上,您会错过MIN_TOKEN与第一个令牌之间以及最后一个令牌与MAX_TOKEN之间的数据。基于令牌的Spark Connector 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
   }