【问题标题】:Using secondary indexes to update rows in Cassandra 2.1在 Cassandra 2.1 中使用二级索引更新行
【发布时间】:2015-07-21 23:49:14
【问题描述】:

我使用的是 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)

当一张票的所有事件都应该移动到另一张票时,我该如何解决这个问题? IE。以下查询不起作用:

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 查询?

我应该使用什么模型来支持这些更改?

【问题讨论】:

    标签: cassandra cassandra-2.1


    【解决方案1】:

    首先,UPDATEINSERT 操作在 Cassandra 中的处理方式相同。它们通俗地称为“UPSERT”。

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

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

    当一张票的所有事件都应该移动到另一张票时,我该如何解决这个问题?

    不幸的是,完成此操作的唯一方法是通过这些键查询events(带有特定的ticket_id)和UPSERT ticket_id 中每一行的键。好消息是您不必先DELETE 他们,因为ticket_id 不是主键的一部分。

    当一张票的所有事件都应该移动到另一张票时,我该如何解决这个问题?

    我认为您最好的计划是完全放弃二级索引,并创建一个查询表以与您的 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。这将为您提供在您的@987654335 上UPSERT 新ticket_id 所需的信息@表。

    然后您还可以通过ticket_id 执行DELETE(在eventsbyticketid 表上)。只要您拥有完整的分区键 (ticket_id),Cassandra 就允许使用部分主键进行 DELETE 操作。所以从查询表中删除旧的ticket_ids 会很容易。并且为了确保写入原子性,您可以将 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 列的一个非常糟糕的名称。

    【讨论】:

      【解决方案2】:

      您可以使用二级索引查询旧票的事件,然后使用这些检索到的事件的主键来更新事件。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-25
        • 1970-01-01
        • 2018-07-01
        • 2019-03-10
        • 1970-01-01
        • 2016-06-29
        • 1970-01-01
        • 2016-09-20
        相关资源
        最近更新 更多