在Cassandra

时间:2016-04-02 04:13:12

标签: sorting cassandra nosql

我最近开始和Cassandra一起玩。我的理解是,在Cassandra表中,您可以定义2个键,可以是单列或复合词:

  1. 分区键:确定如何跨节点分发数据
  2. 聚类密钥:确定写入相同分区密钥(即在同一节点内)的记录的顺序。这也是记录的读取顺序。
  3. 表中的数据将始终按相同的顺序排序,这是集群键列的顺序。因此,必须为特定查询设计表。

    但是如果我需要对表中的数据执行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;

    我知道,根据我创建的表格,这是不可能的,但有哪些替代方案和最佳实践可以解决这个问题?

    解决方案想法

    以下是我想象中产生的一些想法(只是为了表明至少我尝试过):

    • 使用IN子句查询以选择来自许多用户的帖子。这可能有助于Query(1)。使用IN子句时,如果禁用分页,则可以获取全局排序的结果。但是当用户名数量增加时,使用IN子句会很快导致性能下降。
    • 为每个查询维护表的完整副本,每个副本使用自己的PRIMARY KEY,以适应它尝试提供的查询。
    • 将主表与UUID作为分区键。然后为每个查询创建表的较小副本,该副本仅包含对其自己的排序顺序有用的(键)列,以及主表的每一行的UUID。较小的表仅用作"排序索引"查询UUID列表作为结果,然后可以使用主表获取。

    我是NoSQL的新手,我只想知道这样做的正确/持久/有效方式。

3 个答案:

答案 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. 获取所有用户帖子并将其移至单个节点(协调员)
  2. 按日期订购
  3. 排名前N位最新帖子
  4. 要点1.需要全表扫描。实际上,只要您不获取所有记录,就无法实现排序。除非您在插入时使用Cassandra群集列进行排序。但在这种情况下,这意味着所有帖子都存储在同一个分区中,这个分区将永远增长......

    使用非规范化表格或使用新的物化视图功能(http://www.doanduyhai.com/blog/?p=1930

    可以查询SELECT * FROM posts WHERE username='luke' ORDER BY content;

答案 1 :(得分:1)

问题1:

根据您的使用情况,我打赌您可以使用时间段对此进行建模,具体取决于您感兴趣的时间范围。

您可以根据您的使用情况(或更精细的时间间隔)将主键设置为年,年月或年 - 月 -

基本的想法是,您针对您的用例套件进行更改。例如:

  • 如果您经常需要在过去几个月内搜索这些帖子,那么您可能希望将该年份用作PK。
  • 如果您通常需要在过去几天内搜索帖子,那么您可能希望使用年份作为PK。
  • 如果您通常需要在昨天或几天内搜索帖子,那么您可能希望使用一年一月的日作为您的PK。

我将以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;相同,您还必须为此查询创建另一个表。所有列可能与您的第一个表相同,但具有不同的主键,因为您不能按非聚类列的列进行排序。