使用二级索引更新Cassandra 2.1中的行

时间:2015-05-11 12:55:52

标签: cassandra cassandra-2.1

我正在使用Cassandra 2.1并且有一个大致如下的模型:

CREATE TABLE events (
  client_id bigint,
  bucket int,
  timestamp timeuuid,
  ...
  ticket_id bigint,
  PRIMARY KEY ((client_id, bucket), timestamp)
);
CREATE INDEX events_ticket ON events(ticket_id);

如您所见,我在ticket_id上创建了一个辅助索引。这个索引工作正常。 events包含大约1亿行,而这些行中只有500万行有大约50,000个不同的票证。因此,票价平均有100个事件。

查询二级索引无需提供分区密钥即可,这在我们的情况下很方便。由于bucket列有时难以预先确定(即您应该知道事件的日期,bucket目前是日期)。

cqlsh> select * from events where ticket_id = 123;

 client_id | bucket | timestamp | ... | ticket_id
-----------+--------+-----------+-----+-----------

(0 rows)

如果应将故障单的所有事件移动到另一张故障单,我该如何解决问题?即以下查询将无效:

cqlsh> UPDATE events SET ticket_id = 321 WHERE ticket_id = 123;
InvalidRequest: code=2200 [Invalid query] message="Non PRIMARY KEY ticket_id found in where clause"

这是否意味着在UPDATE个查询中无法使用二级索引?

我应该使用什么模型来支持这些变化?

2 个答案:

答案 0 :(得分:3)

首先,UPDATEINSERT操作在Cassandra中被视为相同。它们通俗地称为“UPSERT”。

  

这是否意味着二级索引不能在UPDATE查询中使用?

正确。如果不指定完整的PRIMARY KEY,则无法在Cassandra中执行UPSERT。即使是具有部分PRIMARY KEY的UPSERT也不起作用。并且(正如您所发现的那样)通过索引值进行UPSERT操作也不起作用。

  

如果应将故障单的所有事件移动到另一张故障单,我该如何解决问题?

不幸的是,实现此目的的唯一方法是通过这些键查询events(使用特定ticket_id)和UPSERT ticket_id中每行的键。好的,是你不必先DELETE,因为ticket_id不是PRIMARY KEY的一部分。

  

如果应将故障单的所有事件移动到另一张故障单,我该如何解决问题?

我认为您最好的计划是放弃一个二级索引,并创建一个查询表,与events表一起使用:

CREATE TABLE eventsbyticketid (
  client_id bigint,
  bucket int,
  timestamp timeuuid,
  ...
  ticket_id bigint,
  PRIMARY KEY ((ticket_id), timestamp)
) WITH CLUSTERING ORDER BY (timestamp DESC);

这样您就可以快速查询ticket_id(获取client_idbuckettimestamp。这样可以为您提供UPSERT所需的信息您ticket_id表上的新events

然后,您还可以DELETE执行ticket_ideventsbyticketid表)。只要您拥有完整的分区键(DELETE),Cassandra就允许使用部分PRIMARY KEY进行ticket_id操作。因此,从查询表中删除旧的ticket_id将很容易。为确保写入原子性,您可以将UPSERT一起批处理:

BEGIN BATCH
  UPDATE events SET ticket_id = 321 WHERE client_id=2112 AND bucket='2015-04-22 14:53' AND timestamp=4a7e2730-e929-11e4-88c8-21b264d4c94d;
  UPDATE eventsbyticketid SET client_id=2112, bucket='2015-04-22 14:53' WHERE ticket_id=321 AND timestamp=4a7e2730-e929-11e4-88c8-21b264d4c94d
APPLY BATCH;

实际上与执行相同:

BEGIN BATCH
  INSERT INTO events (client_id,bucket,timestamp,ticketid) VALUES(2112,'2015-04-22 14:53',4a7e2730-e929-11e4-88c8-21b264d4c94d,321);
  INSERT INTO eventsbyticketid (client_id,bucket,timestamp,ticketid) VALUES(2112,'2015-04-22 14:53',4a7e2730-e929-11e4-88c8-21b264d4c94d,321);
APPLY BATCH;

旁注:timestamp实际上是Cassandra中的(保留字)数据类型。这使得它成为timeuuid列的一个非常糟糕的名称。

答案 1 :(得分:1)

您可以使用辅助索引查询旧故障单的事件,然后使用这些检索到的事件中的主键来更新事件。

我不确定你为什么需要手动执行此操作,这似乎是Cassandra应该能够做到的。