使用Cassandra获得最新的独特结果

使用Cassandra获得最新的独特结果,cassandra,Cassandra,我有一个服务可以处理不同服务上的用户状态。 多个DCs上的流量可能非常高,因此我认为Cassandra适合存储这些数据。 我只需要保留每个服务和用户的最新更新。 我想创建这个表: CREATE TABLE db.state ( service uuid, user uuid, updated_at timestamp, data varchar, PRIMARY KEY (service, user, updated_at) ) WITH CLUSTERING O

我有一个服务可以处理不同服务上的用户状态。 多个DCs上的流量可能非常高,因此我认为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个唯一用户状态。
使用此查询:

选择服务、用户、数据,在FROM db.state处更新,其中服务=:服务限制100

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

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

  • 使用
    主键(服务,用户)
    和 使用
    主键(服务、用户、,
    更新地址)
    。但这会影响性能
  • 使用
    主键(服务,用户)
    创建表并读取 在写入之前具有完全一致性,以检查是否未写入较旧的更新。但这就放弃了可用性和一种反模式 卡桑德拉
  • 有没有一种不用先读后写/物化视图的方法


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

    供您选择:

  • 使用主键(service,user)创建主表,并使用主键(service,user,Update_at)创建物化视图。但是 这将影响性能
  • 物化视图并不会对性能造成太大的影响,而且写入路径非常快,所以我不必担心这一点,但MVs当前存在很多问题,并且标记为实验性视图是有原因的——我不推荐它们,否则在当前版本中您将面临很多一致性问题

  • 使用主键(服务、用户)创建表,并使用完整的 写入前保持一致性,以检查是否未写入较旧的更新。 但这让卡桑德拉失去了可用性和反模式
  • 也许我遗漏了一些你没有解释的要求,但你不需要在写之前先读一读。对我来说,这似乎是迄今为止最好的解决办法。当您有一个更新时,将更改推送到(服务,用户)表中,然后当您从表中读取时,您将获得每个用户的最新更新。在使用paxos的insert/update中,总是有
    IF存在
    或IF子句

    如果您需要历史记录(不仅仅是最新的),并且不需要第二张表,您可以使用group by:

    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);
    

    其中bucket是YYYY-MM-DD字符串(或YYYY-WEEKOFYEAR或其他内容)。然后大约在边界时间查询当前和最后一个bucket。否则,分区将不断增长,直到出现问题。

    谢谢您的回答。我补充了解释:“写入不一定是按顺序进行的,所以时间戳是外部提供的。”对于你的问题,我不需要历史记录