首页 文章

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

提问于
浏览
6

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


想象一下,我有一个包含三列的表: pk ,主键, text 类型, foobigintbar ,另一个 text .

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

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


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

update keyspace.table set bar='change' where pk='first';

然后我会看到这种行为 .

pk  | foo | bar
--------------------
first |  1  | change
first |  0  | change   // after the initial 60 seconds

换句话说,永远不会删除该行 . 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

2 回答

  • 7

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

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

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

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

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

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

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

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

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

  • 1

    经过一些测试,这些都是预期的结果 . TTL具有列的粒度 .

    • 进行更新时,如果未指定TTL,则将列TTL设置为0.此操作不会影响其他列TTL .

    • 我们无法更新列值并在单个cql命令中保留旧列值TTL .

    • 当所有列TTL过期时,将删除行(或主/分区键) . 如果列具有TTL或0,则不会删除该行 .

    截至今天(Cassandra 2.1),以下是更新列值并保留其TTL的方法:

    SELECT TTL(col1) FROM table1 where pk=1;
    // read the ttl value fetched.
    UPDATE table1 USING TTL <the_ttl_value> set col1='change' where pk=1;
    

相关问题