Cassandra - 使用 ORDER BY 和 UPDATE 聚类键的替代方法

Cassandra - alternate way for clustering key with ORDER BY and UPDATE

我的架构是:

CREATE TABLE friends (
     userId timeuuid,
     friendId timeuuid,
     status varchar, 
     ts timeuuid,   
     PRIMARY KEY (userId,friendId)
);

CREATE TABLE friends_by_status (
    userId timeuuid,
    friendId timeuuid,
    status varchar, 
    ts timeuuid,   
    PRIMARY KEY ((userId,status), ts)
)with clustering order by (ts desc);

在这里,每当有朋友请求时,我都会在两个 table 中插入记录。 当我想检查一对一的用户状态时,我会使用这个查询:

SELECT status FROM friends WHERE userId=xxx AND friendId=xxx;

当我需要查询所有pending状态的记录时,我会使用:

SELECT * FROM friends_by_status WHERE userId=xxx AND status='pending';

但是,当状态发生变化时,我可以更新 'friends' [=44= 中的 'status' 和 'ts' ],但不在 'friends_by_status' table 中,因为两者都是 PRIMARY KEY 的一部分。

你可以看到,即使我对其进行反规范化,我肯定需要更新 'friends_by_status' table 中的 'status' 和 'ts' 以保持一致性。

保持一致性的唯一方法是删除记录并重新插入。
但是cassandra模型也不推荐频繁删除。 As said in Cassaandra Spottify summit.

我发现这是 Cassandra 中最大的限制。

有没有其他方法可以解决这个问题。

感谢任何解决方案。

为什么第二个 table 的主键中需要状态?如果这是您的架构:

CREATE TABLE friends_by_status (
userId timeuuid,
friendId timeuuid,
status varchar, 
ts timeuuid,   
PRIMARY KEY ((userId), status, ts) 
with clustering order by (ts desc));

您可以根据需要更新状态并仍然按它进行过滤。您将在一个分区下存储更多数据,但似乎您正在为用户拥有的每个朋友存储一行。这与第一个 table 中的相同,因此我认为分区大小不是问题。

我不知道您需要多快部署它,但在 Cassandra 3.0 中,您可以使用物化视图来处理它。您的朋友 table 将是基础 table,而 friends_by_status 将是基础 table 的视图。当您更改基 table.

时,Cassandra 会注意更新视图

例如:

CREATE TABLE friends ( userid int, friendid int, status varchar, ts timeuuid, PRIMARY KEY (userId,friendId) );
CREATE MATERIALIZED VIEW friends_by_status AS
    SELECT userId from friends WHERE userID IS NOT NULL AND friendId IS NOT NULL AND status IS NOT NULL AND ts IS NOT NULL
    PRIMARY KEY ((userId,status), friendID);

INSERT INTO friends (userid, friendid, status, ts) VALUES (1, 500, 'pending', now());
INSERT INTO friends (userid, friendid, status, ts) VALUES (1, 501, 'accepted', now());
INSERT INTO friends (userid, friendid, status, ts) VALUES (1, 502, 'pending', now());
SELECT * FROM friends;                

 userid | friendid | status   | ts
--------+----------+----------+--------------------------------------
      1 |      500 |  pending | a02f7fe0-49f9-11e5-9e3c-ab179e6a6326
      1 |      501 | accepted | a6c80980-49f9-11e5-9e3c-ab179e6a6326
      1 |      502 |  pending | add10830-49f9-11e5-9e3c-ab179e6a6326

所以现在在视图中您可以按状态 select 行:

SELECT * FROM friends_by_status WHERE userid=1 AND status='pending';

 userid | status  | friendid
--------+---------+----------
      1 | pending |      500
      1 | pending |      502

(2 rows)

然后当您在基础 table 中更新状态时,它会自动在视图中更新:

UPDATE friends SET status='pending' WHERE userid=1 AND friendid=501;
SELECT * FROM friends_by_status WHERE userid=1 AND status='pending';

 userid | status  | friendid
--------+---------+----------
      1 | pending |      500
      1 | pending |      501
      1 | pending |      502

(3 rows)

但请注意,在视图中您不能将 ts 作为键的一部分,因为您只能从基础 table 添加一个非键字段作为视图中键的一部分,在您的情况下,这将向密钥添加 'status'。

如果您想尝试一下,我认为 3.0 的第一个测试版将于明天发布。