Cassandra 防止重复

Cassandra preventing duplicates

我有一个简单的 table 分发者 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 次或 none 次访问。 visitTime 只是与之关联的一些数据,用于在 select * from test where userId = ? order by visitTime desc.

等查询中进行排序

如何要求 (userId, placeId) 是唯一的?我需要确保

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

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

对于 Cassandra,每个主键(行键 + 集群键)组合都是唯一的。因此,如果您有一个主键为 (A, B, C) 的条目,然后插入另一个具有相同 (A, B, C) 值的新条目,则旧条目将被覆盖。

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

如果我对您的要求的理解正确,您实际上并不需要 visitTime 成为主键的一部分。在您的查询中,您也不需要按 visitTime 排序,因为 userId/placeId 组合总是只会出现一次。您不需要插入没有 visitTime 的 "record",因为您可以安全地假设如果您的查询结果为 returns 0,那么用户从未访问过这个地方。

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

然后您可以使用一个简单的 insert into test (userId, placeId, timeVisit) values (?, ?, ?) IF NOT EXISTS,如果已经有一条记录具有所提供的 userId/placeId 组合,它不会覆盖。

让我明白——如果这对 (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;

现在table的数据还没有更新,因为夫妻(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,
卡罗