Cassandra 中的这种行为似乎违反直觉,我想知道为什么会发生这种情况,并可能解决这个问题。

假设我有一个包含三列的表: pk ,主键, text 类型, foo ,一个 bigint bar _code ,另一个。

insert into keyspace.table (pk, foo, bar) values ('first', 1, 'test') using ttl 60;

这会在我的表中创建一行,其生存时间为 60 秒。看着它,看起来像这样:
  pk  | foo | bar
------------------
first |  1  | test

现在我这样做:
update keyspace.table using ttl 10 set bar='change' where pk='first';

然后,看着这一行,我看到它发生了以下变化:
  pk  | foo | bar
--------------------
first |  1  | change
first |  1  | <<null>>  // after 10 seconds
   << deleted >>        // after the initial 60 seconds

一切都很好。我想要的是 text 的生存时间改变,但没有别的,尤其是主键。这种行为是意料之中的。

但是,如果我的更新中没有 bar,或者它设置为 0:
update keyspace.table set bar='change' where pk='first';

然后随着时间的推移,我看到了这种行为。
  pk  | foo | bar
--------------------
first |  1  | change
first |  0  | change   // after the initial 60 seconds

换句话说,该行永远不会被删除。 ttl 没有改变,所以它的生存时间仍然有效,并且在它通过后该值被删除(设置为 0)。但是 foo 的生存时间确实发生了变化。这完全出乎意料。

为什么只有当我没有在更新中指定生存时间时,主键的生存时间才会改变?我该如何解决这个问题,以便主键的生存时间只有在我明确表示这样做的情况下才会改变?

编辑我还发现,如果我使用的生存时间高于初始生存时间,它似乎也会改变主键的生存时间。
update keyspace.table using ttl 70 set bar='change' where pk='first';

  pk  | foo | bar
--------------------
first |  1  | change
first |  0  | change   // after the initial 60 seconds
   << deleted >>       // after the 70 seconds

最佳答案

您遇到的效果是由Cassandra使用的存储模型引起的。

在您的示例中,如果您的表没有任何群集列,则该表中的每一行都映射到数据存储区中的一行(通常称为“节俭行”,因为这是通过Thrift API公开的存储模型)。表中不属于主键的每一列(因此在您的示例中为foobar列)都映射到Thrift行中的一列。除此之外,还会创建一个在CQL行中不可见的额外列,作为该行存在的标记。

TTL过期发生在Thrift列的级别上,而不是CQL列的级别上。当您对一行进行INSERT编码时,您插入的所有列以及该行本身的特殊标记都将获得相同的TTL。

如果您对行进行UPDATE,则只有您更新的列会获得新的TTL。未触摸行标记。

使用SELECT运行查询时,将返回至少有一列特殊行标记存在的所有行。这意味着,具有最高TTL的列定义了CQL行可见的时间,除非该行本身的标记(仅在使用INSERT语句时才被触摸)具有更长的TTL。

如果要确保使用与新列值相同的TTL更新行的主键,则解决方法很简单:在更新行时使用INSERT语句。这与使用UPDATE具有完全相同的效果,但也会更新行标记的TTL。

此解决方法的唯一缺点是,它无法与轻量级事务(IFINSERT语句中的UPDATE子句)结合使用。如果您需要将这些与TTL结合使用,则必须使用更复杂的解决方法,但是我想这将是一个单独的问题。

如果要更新某行的某些列,但仍然希望在插入时指定的TTL最初到期后整个行消失,那么Cassandra不直接支持此功能。唯一的方法是先查询一列的TTL,然后在UPDATE操作中使用此TTL,以找出该行剩余的TTL。例如,您可以使用SELECT TTL(foo) FROM table1 WHERE pk = 'first';。但是,这会影响性能,因为它会增加延迟(您必须等待SELECT的结果才能运行UPDATE)。

或者,您可以添加一列,该列仅用作“行存在”标记,并且仅在INSERT期间而不是UPDATE进行触摸。然后,您可以简单地忽略此列为null的行,但是此过滤将需要在客户端实现,并且如果您无法在UPDATE中指定TTL,则将无济于事,因为永远不会删除更新的列。

关于如果未在更新中指定 TTL,则 Cassandra TTL 在主键上设置为 0,但如果是,则主键上的 TTL 不会更改,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27280407/

10-14 10:58