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
仅为 userId
、placeId
,那么您可以使用 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,
卡罗
我有一个简单的 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
仅为 userId
、placeId
,那么您可以使用 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,
卡罗