基于令牌范围的分区键查询的性能?
Performance of token range based queries on partition keys?
我正在根据我的分区键的令牌范围从 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 中,不建议执行范围查询,那么,这个查询是否具有性能?
据我所知,此查询将仅调用单个 partition/vnode 而不会调用多个分区,因此不应该有任何性能问题?这是正确的吗?
Cassandra 版本:3.x
是的,令牌范围 查询,与实际分区键上的普通范围查询相反,确实是高性能的,因为它们可以按顺序从磁盘读取(分区存储在按顺序令牌顺序磁盘)并从同一节点读取顺序数据(相邻令牌属于同一节点)。
Cassandra 提示您这种查询将执行良好,因为它不需要您使用 "ALLOW FILTERING"。如果您尝试对实际的分区键(而不是它的标记)进行范围查询,这将需要您添加一个 "ALLOW FILTERING" 以表明您知道这会导致性能不佳。
对令牌范围的查询是高效的,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
}
我正在根据我的分区键的令牌范围从 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 中,不建议执行范围查询,那么,这个查询是否具有性能?
据我所知,此查询将仅调用单个 partition/vnode 而不会调用多个分区,因此不应该有任何性能问题?这是正确的吗?
Cassandra 版本:3.x
是的,令牌范围 查询,与实际分区键上的普通范围查询相反,确实是高性能的,因为它们可以按顺序从磁盘读取(分区存储在按顺序令牌顺序磁盘)并从同一节点读取顺序数据(相邻令牌属于同一节点)。
Cassandra 提示您这种查询将执行良好,因为它不需要您使用 "ALLOW FILTERING"。如果您尝试对实际的分区键(而不是它的标记)进行范围查询,这将需要您添加一个 "ALLOW FILTERING" 以表明您知道这会导致性能不佳。
对令牌范围的查询是高效的,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
}