Cassandra中的这种行为似乎违反直觉,我想知道为什么会发生这种情况,并可能解决这个问题。
想象一下,我有一个包含三列的表: pk
,主键,text
类型, foo
,一个bigint
和 bar
,另一个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
一切都很好。我想要的是bar
的生存时间改变,但没有别的,特别是不是主键。这种行为是预期的。
但是,如果我的更新中没有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
答案 0 :(得分:9)
您遇到的影响是由Cassandra使用的存储模型引起的。
在您的示例中,如果您的表没有任何聚类列,则表中的每一行都映射到数据存储中的一行(通常称为“Thrift行”,因为这是通过Thrift API)。表中不属于主键的每个列(在您的示例中为foo
和bar
列)都映射到Thrift行中的列。除此之外,还会创建一个在CQL行中不可见的额外列作为该行存在的标记。
TTL过期发生在Thrift列的级别,而不是CQL列。当您INSERT
一行时,您插入的所有列以及该行的特殊标记都会获得相同的TTL。
如果您UPDATE
行,则只有您更新的列会获得新的TTL。未触及行标记。
使用SELECT
运行查询时,将返回至少有一列或存在特殊行标记的所有行。这意味着具有最高TTL的列定义了CQL行可见的时间长度,除非该行本身的标记(仅在使用INSERT
语句时触及)具有更长的TTL。
如果要确保使用与新列值相同的TTL更新行的主键,则解决方法很简单:更新行时使用INSERT
语句。这与使用UPDATE
具有完全相同的效果,但它也会更新行标记的TTL。
此解决方法的唯一缺点是它不能与轻量级事务(IF
或INSERT
语句中的UPDATE
子句)结合使用。如果你需要将它们与TTL结合使用,你必须使用更复杂的解决方法,但我认为这是一个单独的问题。
如果要更新某行的某些列,但仍希望整个行在最初插入时指定的TTL消失,则Cassandra不会直接支持。唯一的方法是首先查询其中一列的TTL,然后在UPDATE
操作中使用此TTL,找出该行的TTL。例如,您可以使用SELECT TTL(foo) FROM table1 WHERE pk = 'first';
。但是,这会影响性能,因为它会增加延迟(您必须等待SELECT
的结果才能运行UPDATE
)。
作为替代方案,您可以添加一个仅用作“行存在”标记的列,并且只在INSERT
期间触摸,而不在UPDATE
中触摸。然后,您可以简单地忽略此列为null
的行,但此过滤需要在客户端实现,如果您无法在UPDATE
中指定TTL,则无效,因为已更新列永远不会被删除。
答案 1 :(得分:2)
经过一些测试,这些都是预期的结果。 TTL具有列的粒度。
截至今天(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;