卡桑德拉防止重复

时间:2015-03-04 13:56:46

标签: cassandra cql cql3

我有一个由userId分发的简单表:

create table test (
  userId uuid,
  placeId uuid,
  visitTime timestamp,
  primary key(userId, placeId, visitTime)
) with clustering order by (placeId asc, visitTime desc);

每对(userId, placeId)可以有1次访问或没有访问。 visitTime只是与之关联的一些数据,用于在select * from test where userId = ? order by visitTime desc等查询中进行排序。

我如何才能要求(userId, placeId)独一无二?我需要确保

insert into test (userId, placeId, timeVisit) values (?, ?, ?)

不会在不同的时间插入(userId, placeId)第二次访问。在插入不是原子之前检查是否存在,有更好的方法吗?

3 个答案:

答案 0 :(得分:4)

让我明白一下 - 如果情侣(userId, placeId)应该是唯一的,(意味着您不必将这两对数据放入两行),timeVisit对于首要的关键?如果只有一行,为什么要使用order by visitTime desc执行查询?

如果你需要的是防止重复,你有两种方法。

1 - 轻量级交易 - 使用IF NOT EXISTS,您可以按照自己的意愿行事。但正如我解释here轻量级事务由于cassandra的特定处理而非常缓慢

2 - USING TIMESTAMP写入时间强制执行 - (小心它!***)'技巧'强制减少TIMESTAMP

让我举个例子:

INSERT INTO users (uid, placeid , visittime , otherstuffs ) VALUES ( 1, 2, 1000, 'PLEASE DO NOT OVERWRITE ME') using TIMESTAMP 100;

这会产生此输出

select * from users;

 uid | placeid | otherstuffs                | visittime
-----+---------+----------------------------+-----------
   1 |       2 | PLEASE DO NOT OVERWRITE ME |      1000

现在让我们减少timestamp

INSERT INTO users (uid, placeid , visittime , otherstuffs ) VALUES ( 1, 2, 2000, 'I WANT OVERWRITE YOU') using TIMESTAMP 90;

现在表格中的数据尚未更新,因为情侣(uid, placeid)的TS操作(100)更高 - 事实上这里输出没有改变

select * from users;

 uid | placeid | otherstuffs                | visittime
-----+---------+----------------------------+-----------
   1 |       2 | PLEASE DO NOT OVERWRITE ME |      1000

如果性能很重要,那么使用解决方案2,如果性能无关紧要,则使用解决方案1.对于解决方案2,您可以使用固定数量减去系统时间毫秒来计算每次写入的减少时间戳

例如:

Long decreasingTimestamp = 2_000_000_000_000L - System.currentTimeMillis();

***例如,如果要删除然后重新插入数据,此解决方案可能会导致意外行为。重要的是要知道,一旦删除数据,只有当写操作具有更高的删除时间戳时才能再次写入数据(如果没有指定,则使用的时间戳是机器的时间戳)

HTH,
卡罗

答案 1 :(得分:1)

使用Cassandra每个主键(行键+聚类键)组合是唯一的。因此,如果您有一个带有主键(A,B,C)的条目,并且您插入另一个具有相同(A,B,C)值的新条目,则旧条目将被覆盖。

在您的情况下,主键中有一个timeVisit属性,这使得在您的情况下这不可用。您可能需要重新考虑您的方案,以便将timeVisit属性保留。

答案 2 :(得分:0)

如果我理解了您的要求,那么您真的不需要visitTime成为主键的一部分。在您的查询中,您也不需要按visitTime排序,因为userId / placeId组合始终只会出现一次。您不需要插入"记录"没有visitTime,因为你可以放心地假设如果你的查询返回0结果,那么用户从未访问过这个地方。

因此,如果您使PRIMARY KEY仅为userIdplaceId,那么您可以使用lightweight transactions来完成目标。

您可以使用简单的insert into test (userId, placeId, timeVisit) values (?, ?, ?) IF NOT EXISTS,如果已经提供了userId / placeId组合的记录,则不会覆盖。