我有一个包含以下架构的表。
CREATE TABLE IF NOT EXISTS group_friends(
groupId timeuuid,
friendId bigint,
time bigint,
PRIMARY KEY(groupId,friendId));
如果组中发生任何更改(例如更改组名或在表中添加新朋友等),我需要跟踪时间。因此,每当任何相关表中发生任何更改时,我都需要通过groupId更新时间字段的值。
由于cassandra中的更新需要提及where子句中的所有主键,因此该查询将不会运行。
update group_friends set time = 123456 where groupId = 100;
所以我可以这样做。
update group_friends set time=123456 where groupId=100 and friendId in (...);
但它显示以下错误 - >
[Invalid query] message="Invalid operator IN for PRIMARY KEY part friendid"
有没有办法在群集列中使用IN运算符执行更新操作?如果没有,那么可能的方法是什么?
提前致谢。
答案 0 :(得分:3)
由于friendId是一个聚类列,因此在这种情况下批处理操作可能是一个合理且性能良好的选择,因为所有更新都将在同一个分区中进行(假设您使用相同的组ID进行更新)。例如,使用java驱动程序,您可以执行以下操作:
Cluster cluster = new Cluster.Builder().addContactPoint("127.0.0.1").build();
Session session = cluster.connect("friends");
PreparedStatement updateStmt = session.prepare("update group_friends set time = ? where groupId = ? and friendId = ?");
long time = 123456;
UUID groupId = UUIDs.startOf(0);
List<Long> friends = Lists.newArrayList(1L, 2L, 4L, 8L, 22L, 1002L);
BatchStatement batch = new BatchStatement(BatchStatement.Type.UNLOGGED);
for(Long friendId : friends) {
batch.add(updateStmt.bind(time, groupId, friendId));
}
session.execute(batch);
cluster.close();
这样做的另一个好处是,由于可以从BatchStatement推断出分区键,因此驱动程序将使用令牌感知路由将请求发送到拥有此数据的副本,从而跳过网络跃点。
虽然这实际上只是一次写入,但要注意批量的大小。你应该注意不要太大。
在一般情况下,通过单独执行每个语句而不是使用批处理,您实际上不会出错。 CQL传输允许单个连接上的许多请求,并且本质上是异步的,因此您可以一次执行许多请求,而不会出现每个连接请求的典型性能成本。
有关批量编写数据的详细信息,请参阅:Cassandra: Batch loading without the Batch keyword
或者,可能有更简单的方法来完成你想要的。如果您真正想要实现的目标是维持群组更新时间,并且您希望群组中的所有朋友都能保持相同,那么您可以将时间设为static column。这是Cassandra 2.0.6中的一项新功能。这样做是为了分享groupId分区中所有行的列值。这样你只需要更新一次时间,甚至可以在用于将朋友添加到组中的查询中设置时间,这样就可以完成一次写操作。
CREATE TABLE IF NOT EXISTS friends.group_friends(
groupId timeuuid,
friendId bigint,
time bigint static,
PRIMARY KEY(groupId,friendId)
);
如果你还不能使用Cassandra 2.0.6+,你可以创建一个名为group_metadata的独立表来维护组的时间,即:
CREATE TABLE IF NOT EXISTS friends.group_metadata(
groupId timeuuid,
time bigint,
PRIMARY KEY(groupId)
);
这方面的缺点是,无论何时想要获取此数据,您都需要从此表中进行选择,但这似乎是可以管理的。