我最近开始和Cassandra一起玩。我的理解是,在Cassandra表中,您可以定义2个键,可以是单列或复合词:
表中的数据将始终按相同的顺序排序,这是集群键列的顺序。因此,必须为特定查询设计表。
但是如果我需要对表中的数据执行2个不同的查询呢?使用Cassandra时解决这个问题的最佳方法是什么?
我们说我有一个简单的表格,其中包含用户撰写的帖子:
CREATE TABLE posts (
username varchar,
creation timestamp,
content varchar,
PRIMARY KEY ((username), creation)
);
此表是"设计"执行以下查询,这对我来说非常有用:
SELECT * FROM posts WHERE username='luke' [ORDER BY creation DESC];
但是,如果我需要按时间顺序获取所有帖子而不管用户名如何:
查询(1): SELECT * FROM posts ORDER BY creation;
或按内容的字母顺序获取帖子:
查询(2): SELECT * FROM posts WHERE username='luke' ORDER BY content;
我知道,根据我创建的表格,这是不可能的,但有哪些替代方案和最佳实践可以解决这个问题?
以下是我想象中产生的一些想法(只是为了表明至少我尝试过):
我是NoSQL的新手,我只想知道这样做的正确/持久/有效方式。
答案 0 :(得分:2)
SELECT * FROM posts ORDER BY creation;
将导致完整群集扫描,因为您未提供任何分区密钥。此查询中的ORDER BY
子句无论如何都不会起作用。
您的要求I need to get all posts regardless of the username, in order of time
在分布式系统中很难实现,它假设为:
要点1.需要全表扫描。实际上,只要您不获取所有记录,就无法实现排序。除非您在插入时使用Cassandra群集列进行排序。但在这种情况下,这意味着所有帖子都存储在同一个分区中,这个分区将永远增长......
使用非规范化表格或使用新的物化视图功能(http://www.doanduyhai.com/blog/?p=1930)
可以查询SELECT * FROM posts WHERE username='luke' ORDER BY content;
答案 1 :(得分:1)
问题1:
根据您的使用情况,我打赌您可以使用时间段对此进行建模,具体取决于您感兴趣的时间范围。
您可以根据您的使用情况(或更精细的时间间隔)将主键设置为年,年月或年 - 月 -
基本的想法是,您针对您的用例套件进行更改。例如:
我将以yyyy-mm-dd作为PK给出一个充实的例子:
现在的表格是:
CREATE TABLE posts_by_creation (
creation_year int,
creation_month int,
creation_day int,
creation timeuuid,
username text, -- using text instead of varchar, they're essentially the same
content text,
PRIMARY KEY ((creation_year,creation_month,creation_day), creation)
)
我将创建更改为timeuuid,以保证每个帖子创建事件的唯一行。如果我们只使用时间戳,理论上你可以在这里覆盖现有的帖子创建记录。
现在我们可以根据当前创建时间插入分区键(PK):creation_year,creation_month,creation_day:
INSERT INTO posts_by_creation (creation_year, creation_month, creation_day, creation, username, content) VALUES (2016, 4, 2, now() , 'fromanator', 'content update1';
INSERT INTO posts_by_creation (creation_year, creation_month, creation_day, creation, username, content) VALUES (2016, 4, 2, now() , 'fromanator', 'content update2';
now()是一个用于生成timeUUID的CQL函数,您可能希望在应用程序中生成此函数,并为PK解析yyyy-mm-dd,然后将timeUUID插入到聚簇列中。 / p>
对于使用此表的使用案例,我们假设您希望今天看到所有更改,您的CQL将如下所示:
SELECT * FROM posts_by_creation WHERE creation_year = 2016 AND creation_month = 4 AND creation_day = 2;
或者如果你想在今天下午5点之后找到所有的变化:
SELECT * FROM posts_by_creation WHERE creation_year = 2016 AND creation_month = 4 AND creation_day = 2 AND creation> = minTimeuuid(' 2016-04-02 5:00-0600');
minTimeuuid()是另一个cql函数,它将在给定时间内创建尽可能小的timeUUID,这将保证您从那时起获得所有更改。
根据时间跨度,您可能需要查询几个不同的分区键,但它不应该难以实现。此外,您还希望将创建列更改为其他表的timeuuid。
问题2:
您必须创建另一个表或使用物化视图来支持这种新的查询模式,就像您想的那样。
最后,如果你没有使用Cassandra 3.x +或者不想使用物化视图,你可以使用Atomic批次来确保几个非规范化表格的数据一致性(这就是它的设计目标) )。因此,在您的情况下,它将是一个BATCH语句,其中3个相同数据的插入到3个支持您的查询模式的不同表。
答案 2 :(得分:1)
解决方案是创建另一个表来支持您的查询。
对于SELECT * FROM posts ORDER BY creation;
,您可能需要一些特殊列来对其进行分组,可能需要按月份和年份进行分组,例如PRIMARY KEY((year, month), timestamp)
这样cassandra在读取时会有更好的性能,因为它不需要扫描整个集群来获取所有数据,它也会保存节点之间的数据传输。
与SELECT * FROM posts WHERE username='luke' ORDER BY content;
相同,您还必须为此查询创建另一个表。所有列可能与您的第一个表相同,但具有不同的主键,因为您不能按非聚类列的列进行排序。