如果在更新时未指定TTL,则Cassandra TTL在主键上设置为0,但如果是,则主键上的TTL不会更改

2rs*_*2ts 7 cql ttl cassandra cql3 cassandra-2.0

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


想象一下,我有一个包含三列的表:pk主键,text类型foo,a bigint,和bar另一个text.

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

这会在我的表中创建一行,其生存时间为60秒.看着它,它看起来像这样:

  pk  | foo | bar
------------------
first |  1  | test
Run Code Online (Sandbox Code Playgroud)

现在我做:

update keyspace.table using ttl 10 set bar='change' where pk='first';
Run Code Online (Sandbox Code Playgroud)

然后,看着这行,我看到它经历了以下变化:

  pk  | foo | bar
--------------------
first |  1  | change
first |  1  | <<null>>  // after 10 seconds
   << deleted >>        // after the initial 60 seconds
Run Code Online (Sandbox Code Playgroud)

一切都很好.我想要的是bar改变生存的时间,但没有别的,特别是不是主键.这种行为是预期的.


但是,如果我的更新中没有ttl,或者它设置为0:

update keyspace.table set bar='change' where pk='first';
Run Code Online (Sandbox Code Playgroud)

然后我会看到这种行为.

  pk  | foo | bar
--------------------
first |  1  | change
first |  0  | change   // after the initial 60 seconds
Run Code Online (Sandbox Code Playgroud)

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

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

编辑我还发现如果我使用的时间比初始时间高,它似乎也会改变主键的生存时间.

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
Run Code Online (Sandbox Code Playgroud)

Seb*_*ing 10

您遇到的影响是由Cassandra使用的存储模型引起的.

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

TTL过期发生在Thrift列的级别,而不是CQL列.当您INSERT连续时,您插入的所有列以及该行的特殊标记都会获得相同的TTL.

如果您UPDATE连续,只有您更新的列会获得新的TTL.未触摸行标记.

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

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

此解决方法的唯一缺点是它不能与轻量级事务(IF子句INSERTUPDATE语句中)一起使用.如果你需要将它们与TTL结合使用,你必须使用更复杂的解决方法,但我认为这是一个单独的问题.

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

作为替代方案,您可以添加一个仅用作"行存在"标记的列,并且只在该期间触摸,INSERT而从不在期间触摸UPDATE.然后,您可以简单地忽略此列null所属的行,但是此过滤需要在客户端实现,如果您不能在a中指定TTL,则无效,UPDATE因为更新的列永远不会被删除.

  • 基本上,即使存在集群密钥,也是如此。但是,我在回答中提出的某些主张不再有效,例如,如果有一个集群键,则具有相同分区键的所有CQL行实际上将映射到同一Thrift行。如果要查找一组特定的CQL行和列如何映射到基础数据存储,最好使用“ cassandra_cli”查看数据。这将暴露通过CQL接口不可见的内部细节。 (2认同)