ScyllaDB查询百亿行(高基数)的效率

Efficiency of Querying 10 Billion Rows (with High Cardinality) in ScyllaDB

假设我有一个 table,其中有 100 亿行分布在 100 台机器上。 table 具有以下结构:

PK1 PK2 PK3 V1 V2

其中 PK 表示分区键,V 表示值。所以在上面的例子中,分区键由3列组成。

Scylla 要求您在 WHERE 子句中指定分区键的所有列。

如果您想在仅指定部分列的同时执行查询,您会收到警告,因为这需要完整的 table 扫描:

SELECT V1 & V2 FROM table WHERE PK1 = X & PK2 = Y

在上面的查询中,我们只指定了 3 列中的 2 列。假设查询匹配 100 亿行中的 10 亿行 - 考虑此查询的 cost/performance 的良好心智模型是什么?

我的假设是成本高:相当于对数据集执行百亿个单独的查询,因为1)数据中的行之间没有逻辑关联行存储到磁盘的方式,因为每一行都有不同的分区键(高基数)2) 以便 Scylla 确定哪些行匹配它必须扫描所有 100 亿行的查询(即使结果集只匹配了 10 亿行)

假设单个服务器每秒可以处理 100K 个事务(完全在 ScyllaDB 人宣传的范围内)并且数据驻留在 100 个服务器上,处理此查询的(估计)时间可以计算为:100K * 100 = 每秒 1000 万次查询。 100 亿除以 10M = 1,000 秒。所以它需要集群大约。处理查询需要 1,000 秒(消耗所有集群资源)。

这是正确的吗?还是我的 Scylla 如何处理此类查询的心智模型存在任何缺陷?

谢谢

正如您自己建议的那样,Scylla(我将在回答中说的所有内容也适用于 Cassandra)通过包含三列的完整分区键对分区进行哈希处理。 ּ所以Scylla没有有效的方法来扫描匹配的分区。它必须扫描所有分区,并检查每个分区是否 partition-key 匹配请求。

然而,这并不意味着它像“对数据执行一百亿个单独的查询”一样效率低下。扫描 100 亿个分区通常(当每一行的数据本身不是很大时)比执行 100 亿 random-access 次读取更有效,每次读取单独读取一个分区。 random-access 读取有很多工作 - Scylla 需要到达协调器,然后将其发送到副本,每个副本需要在其 one-disk 数据文件(通常是多个文件)中找到特定位置,经常需要从磁盘 over-read (因为磁盘和压缩对齐需要),等等。与此相比,扫描 - 它可以从磁盘读取按令牌(partition-key 哈希)排序的长连续数据,并且可以 return 相当快地使用更少的 I/O 操作和更少的 CPU工作。

因此,如果您的示例设置每个节点可以执行 100,000 random-access 次读取,那么它在扫描期间每秒可能读取超过 100,000 行。我不知道给你哪个确切的数字,但是博客 post https://www.scylladb.com/2019/12/12/how-scylla-scaled-to-one-billion-rows-a-second/ 我们(完全披露:我是 ScyllaDB 开发人员)展示了一个每秒扫描十亿(!)行的示例用例只有 83 个节点 - 每个节点每秒 1200 万行,而不是您估计的 100,000 行。因此,您的示例用例可能会在 8.3 秒内结束,而不是您计算的 1000 秒。

最后,请不要忘记(在前面提到的博客 post 中也提到了这一点),如果您进行大型扫描,您应该明确 parallelize,即,将令牌范围分成几部分然后并行扫描。首先,显然没有一个客户端能够处理每秒扫描 10 亿个分区的结果,因此这种并行化是 more-or-less 不可避免的。其次,按分区顺序扫描 returns 个分区,这些分区(正如我上面所解释的)连续位于单个副本上——这对于峰值吞吐量非常有用,但也意味着只有一个节点(甚至一个 CPU)将在扫描期间的任何时间处于活动状态。因此,将扫描分成几部分并并行进行所有扫描非常重要。我们还有一篇博客 post 介绍并行扫描的重要性以及如何进行:https://www.scylladb.com/2017/03/28/parallel-efficient-full-table-scan-scylla/.

另一种选择是移动一个主键成为聚类键,这样如果你有前两个主键,你就可以找到分区,然后用它搜索