为什么我不能在过滤主键后在 Cassandra 中添加 WHERE 子句?

Why can't I add WHERE clauses in Cassandra after filtering on the primary key?

编辑*感谢陌生人的代码格式化,以后我会牢记!

我正在学习基本的 planetcassandra.org Cassandra 教程,但我不明白为什么我无法执行以下查询:

select * 
from users 
where lastname = 'Smith' AND city = 'X';

关于这个 table:

CREATE TABLE users 
(
    firstname text,
    lastname text,
    age int,
    email text,
    city text,
    PRIMARY KEY (lastname)
);

据我了解,分区键(姓氏)对数据进行分区。所以所有姓氏为 Smith 的行都应该在节点 X 上。是什么阻止我按城市进一步过滤这些行?

谢谢!

简答

您必须有一个聚类列 -- city。

更新:对简洁的回复表示歉意。让我充实一下。

Cassandra 在磁盘上顺序存储数据(快速深入了解 C* 读取路径)

Cassandra 是从头开始构建的分布式系统,专为高性能和可用性而设计。虽然基于 SQL,但 CQL 在您可以和不能执行的查询类型方面受到限制,并且通常您必须围绕工作负载/访问模式围绕查询模式(和重复数据)设计数据模型。

的确,一旦您在 cql where 子句中指定了分区键,cassandra 就知道您的数据存储在哪个节点中。但是,它仍然必须在该节点中找到数据。

请记住,C* 基于列的集群顺序存储数据。为了找到您正在寻找的 CQL 行,cassandra 必须在磁盘上进行完整的查找,一旦您售罄并拥有大量数据,这会很慢。如果您有聚类列 x、y 和 z,则数据将分别按三个聚类列排序。这就是为什么您只能按顺序包含 x、y 和 z 的 where 约束。

查看 this data modeling tool 可视化 c* 存储层的数据模型,查看可能的查询,并生成 stress-yaml。

您的问题在这里有两个答案。一个特定于您的示例,一个更通用的答案(这可能是您真正想要的)。

回答你的例子

在您的具体示例中,您有一个主键 "lastname"。所以在这种情况下,每个分区只有一行。任何时候用姓氏 "Smith" 更新行时,都会覆盖该行中以前的所有数据。在那种情况下,where 子句实际上没有意义,因为当您查询 "Smith" 行时,只会有一个结果。

更笼统的回答

我猜你的意思是你的例子允许每个分区不止一行。可能类似于 PRIMARY KEY (lastname, user_id)(或集群键中的任何列,可以让您识别具有相同姓氏的不同用户)。

Cassandra 中的分区可能非常大。单个分区中可能有数百万行。主键中的集群列决定了这些行在磁盘上存储时的排序方式。因此,当您对聚类列进行查询时,Cassandra 可以使用数据排序知识来精确找到您要查找的数据。

如果 Cassandra 允许查询不在集群键中的列,则需要扫描分区内的所有数据并根据您的查询检查每一行。这将是极其低效的。

要进一步扩展集群列,集群列的实际顺序也很重要。如上所述,排序决定了行在磁盘上的存储方式。所以 "PRIMARY KEY (a, b, c)" 和 "PRIMARY KEY (a, c, b)" 相同。在第一个示例中,磁盘上的行首先按 "b" 列排序,然后 "b" 列具有相同值的所有行按 "c" 列排序。这意味着如果不指定 "b",则无法在分区内查询具有 "c" 特定值的列。该查询将再次需要扫描整个分区,因为行首先按 "b" 排序。

预先了解您想要执行的确切查询将帮助您确定所需的聚类键以及是否需要非规范化为多个表以支持多个查询。