cassandra - 使用 Cassandra 获取最新的独特结果

标签 cassandra

我有一个服务可以处理不同服务上的用户状态。 多个 DC 上的流量可能非常高,因此我认为 Cassandra 适合存储这些数据。
我只需要保留每个服务和用户的最新更新。
我考虑过创建这个表:

CREATE TABLE db.state (
   service uuid,
   user uuid,
   updated_at timestamp,
   data varchar,

   PRIMARY KEY (service, user, updated_at)
) WITH CLUSTERING ORDER BY (updated_at DESC);

问题是如何查询最新的 100 个唯一用户状态。
使用此查询:

从 db.state WHERE service = :service LIMIT 100 中选择服务、用户、数据、updated_at

如果某个用户有很多更新,我不会获得最新的 100 个用户,而是会更少。 我不想合并客户端中的唯一用户,因为为了获得 100 个用户,我有时需要获得 10000 行。

我想到了两种都有问题的解决方案:

  1. 使用主键(服务,用户)创建主表并 使用主键(服务、用户、 update_at)。但这会损害性能。
  2. 使用主键(服务,用户)创建表并读取 在写入之前保持完全一致性,以检查是否未写入较旧的更新。但这放弃了可用性和反模式 Cassandra 。

有没有办法在没有先读后写/物化 View 的情况下做到这一点?

<小时/>

编辑

写入不一定按顺序进行,因此时间戳是外部提供的。
我不需要保留历史记录,只需保留最后一次更新(通过外部时间戳)。

最佳答案

供您选择:

  1. create the main table with PRIMARY KEY (service, user) and create materialized view with PRIMARY KEY (service, user, updated_at). But this will hurt performance.

物化 View 并不会真正影响性能,而且写入路径非常快,所以我不会担心这一点,但目前 MV 存在很多问题,并且出于某种原因标记为实验性 - 我不会推荐它们,否则您将面临当前版本存在很多一致性问题。

  1. create the table with PRIMARY KEY (service, user) and read with full consistency before write to check that older update is not written. But this gives up availability and an anti-pattern for Cassandra.

也许我遗漏了一些您没有解释的要求,但您不需要在写入之前进行读取。在我看来,这似乎是迄今为止最好的解决方案。当您有更新时,将更改推送到(服务,用户)表,然后当您从表中读取时,您将获得每个用户的最新更新。使用 paxos 进行插入/更新时始终存在 IF EXISTS 或 IF 子句。

如果您需要历史记录(不仅仅是最新的)并且不需要第二个表,您可以使用分组依据:

CREATE TABLE state (  // simplified a little
   service int,
   user int,
   updated_at timeuuid,
   data text,
   PRIMARY KEY (service, user, updated_at)
) WITH CLUSTERING ORDER BY (user ASC, updated_at DESC);

INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 1, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 1, now(), '2');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 1, now(), '3');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 2, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 2, now(), '2');
INSERT INTO state (service, user, updated_at, data) VALUES ( 2, 1, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '2');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '3');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '1');
INSERT INTO state (service, user, updated_at, data) VALUES ( 1, 3, now(), '2');

SELECT * FROM state WHERE service = 1 GROUP BY service, user;

 service | user | updated_at                           | data
---------+------+--------------------------------------+------
       1 |    1 | 7c2bd900-981e-11e9-a27a-7b01c564a3f0 |    3
       1 |    2 | 7c2d1180-981e-11e9-a27a-7b01c564a3f0 |    2
       1 |    3 | 7c88c610-981e-11e9-a27a-7b01c564a3f0 |    2

它的效率并不高,但只要您不让单个服务分区变得太大,它就会起作用。我实际上强烈建议向其中添加日期组件/存储桶,例如:

CREATE TABLE state (
   bucket text
   service int,
   user int,
   updated_at timeuuid,
   data text,
   PRIMARY KEY ((bucket, service), user, updated_at)
) WITH CLUSTERING ORDER BY (user ASC, updated_at DESC);

其中存储桶是 YYYY-MM-DD 字符串(或 YYYY-WEEKOFYEAR 等)。然后,在边界时间附近,您查询当前和最后一个存储桶。否则,分区将会不断增大,直至引发问题。

关于cassandra - 使用 Cassandra 获取最新的独特结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56767056/

相关文章:

cassandra - 如何仅使用分区键更新 cassandra 中的行?

应用程序日志的 Cassandra 数据模型(数十亿次操作!)

cassandra 2.0.6,commitlog目录215G用于112G数据

cassandra - Hector 查询列族中的所有数据

cassandra - 用于从节点收集指标的 Datastax OpsCenter 默认一致性级别

amazon-ec2 - 如何在云中设置 cassandra 集群

ssl - Cassandra 抛出 SSLFactory.java 警告

Cassandra 最大现实 blob 大小

cassandra - 通过Shell脚本执行CQL?

node.js - 从 NodeJS 使用 UUID 查询 Cassandra