Cassandra 复合聚类键和排序查询

Posted

技术标签:

【中文标题】Cassandra 复合聚类键和排序查询【英文标题】:Cassandra compound clustering key and queries with ordering 【发布时间】:2015-01-13 22:05:16 【问题描述】:

我们大量使用 cassandra 宽行来存储每个用户的时间序列,因为它们非常适合该用例。假设我们有一张桌子:

create table user_events ( user_id text, timestmp timestamp, event text, primary key((user_id), timestmp));

如果可能发生时间戳冲突怎么办(同一用户可以发出具有相同时间戳的两个不同事件)。假设我们对所有存在的事件都有一个排序(每个事件都有一个序列 int),那么调整此架构以解决该问题的最佳方法是什么。

如果我通过以下方式修改架构:

create table user_events ( user_id text, timestmp timestamp, seq int, event text, primary key((user_id), timestmp, seq));

我不能这样做 WHERE user_id = ? ORDER BY timestmp ASC, seq ASC - cassandra 不允许这样做。

【问题讨论】:

WHERE user_id = ? ORDER BY timestmp ASC, seq ASC - 在 cassandra 中可以进行此查询(对于您的第二个模式),例如: select * from user_events where user_id = 'xyz' order by timestmp ASC;这将以timestmp 的升序返回。如果用户“xyz”在同一时间戳执行超过 2 个事件,则 seq 值将按插入顺序进行维护。 【参考方案1】:

我将无法执行 WHERE user_id = ? ORDER BY timestmp ASC, seq ASC – cassandra 不允许这样做。

您可能会看到一个错误,因为您正在重复 ASC。这应该有效:

WHERE user_id = ? ORDER BY timestmp,seq ASC

此外,只要您将主键定义为PRIMARY KEY((user_id),timestmp,seq)),您甚至不需要指定ORDER BY x[,y] ASC。它将按该顺序对磁盘上的数据进行聚类,从而将其返回给已按该顺序排序的您。 ORDER BY 仅在您希望将结果按降序排列(或与您定义的相反顺序)时才需要。

如果可能发生时间戳冲突怎么办?

我认为您额外的 seq 列应该足够了,具体取决于您计划如何插入数据。如果您从客户端设置timestmp,那么您应该没问题。但是,看看当我(使用你的第二个表)INSERT 行同时以两种不同的方式创建时间戳时会发生什么。

INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('Mal',dateof(now()),1,'commanding');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('Wash',dateof(now()),1,'piloting');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('River',dateof(now()),1,'freaking out');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('River',dateof(now()),3,'being weird');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('River',dateof(now()),2,'killing reavers');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('River','2015-01-13 13:14-0600',1,'freaking out');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('River','2015-01-13 13:14-0600',3,'being weird');
INSERT INTO user_events(user_id,timestmp,seq,event) VALUES ('River','2015-01-13 13:14-0600',2,'killing reavers');

通过“River”的user_id 查询该数据会得到:

aploetz@cqlsh:***> SELECT * FROM user_events WHERE user_id='River';
 user_id | timestmp                 | seq | event
---------+--------------------------+-----+-----------------
   River | 2015-01-13 13:14:00-0600 |   1 |    freaking out
   River | 2015-01-13 13:14:00-0600 |   2 | killing reavers
   River | 2015-01-13 13:14:00-0600 |   3 |     being weird
   River | 2015-01-14 12:58:41-0600 |   1 |    freaking out
   River | 2015-01-14 12:58:57-0600 |   3 |     being weird
   River | 2015-01-14 12:58:57-0600 |   2 | killing reavers

(6 rows)

请注意,使用now() 函数生成timeuuid,然后将其转换为带有dateof() 的时间戳会导致出现timestmp 为“2015-01-14 12:58:57-0600”的两行是一样的。但它们并不相同,您可以通过seq 列看出这一点。

所以在使用/生成时间戳时要小心一点。它们可能看起来相同,但它们可能不会存储为相同的值。为了安全起见,我会改用 timeuuid。

【讨论】:

以上是关于Cassandra 复合聚类键和排序查询的主要内容,如果未能解决你的问题,请参考以下文章

聚类键的粒度级别(高唯一值)

Cassandra 数据模型

使用 IF NOT EXISTS 使用主键和聚类列进行唯一插入

聚类键列必须与 CLUSTERING ORDER BY 指令中的列完全匹配

Spark cassandra 连接器 + 加入超时

是否可以从分区中的每个聚类键Y中选择X记录?