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 的第一个测试版将于明天发布。
我的架构是:
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 的第一个测试版将于明天发布。