How is data updated?
Cassandra将每个新行都当做一个upsert:如果一个新行和一个已有的行有相同的primary key,Cassandra就会对这已有的行进行更新操作。
写数据时,Cassandra将每个新行都添加到数据库中,而不去检查是否有重复的记录存在。这个方法将有可能导致相同行在数据库中有多个版本存在。
存储在内存中的row会周期性地被flush到磁盘中的叫做SSTables的结构文件中。Cassandra周期性地将小的SSTables compacts成大的SSTables。如果在这个过程中,Cassandra遇到同一行数据的2个或多个版本,Cassandra只会将最新版本的数据写到新的SSTable。compaction结束后,Cassandra会删除掉原来的SSTables,以删除过时的row。
Cassandra的大部分生产环境下,会将每一行的副本存在两个或多个节点上。每个节点都独立的执行compaction。这意味着即使一个过时版本的行数据在某个节点上被删除了,可能仍然存在于其他节点上。
这就是为什么Cassandra会在读操作过程中会执行另外一轮比较。当一个客户端根据一个特定的主键请求数据时,Cassandra会从一个或多个副本中拿到多个版本的行数据。带有最新的时间戳的数据是唯一会被返回到客户端的数据(“last-write-wins”)。
注:数据库操作可能只会更新一行数据的部分列,因此有些版本的行数据只包括部分列,而不是全部。在compaction或write阶段,Cassandra收集各个更新操作来组成一个完整的行数据,每一列都使用最新的版本。
How is data deleted?
为了提高性能,并考虑到数据分布,和故障容忍等内置特性,Cassandra这样实现删除数据:
Cassandra将一个删除视为一个insert或者upsert。使用DELETE命令删除的数据会被加上一个删除标记,叫做墓碑。墓碑标记走的也是写过程,会被写到一个或多个节点的SSTables。墓碑最主要的不同点在于:它有一个内置的过期时间(grace period),compaction过程会将过期的墓碑(所对应的数据真正)删除掉。
Deletion in a distributed system
在一个多节点的集群中,Cassandra可以在2个或者更多的节点上存储一份数据的多个副本。这样可以防止数据丢失,但是会使得删除过程变得复杂。如果一个节点接收到一个record的删除操作,节点将这个记录标记为墓碑,然后尝试将墓碑传递给其他包含这个记录的节点。但是如果某个存有该数据的节点在这个时间点没有应答,不能够马上收到墓碑,因此它仍会有这些记录被删除前的版本。如果在这个节点recover之前,集群中其他被标记的墓碑数据都已经被真正删除了,Cassandra会将recovered这个节点上的这个记录作为新的数据,并且将它传送到集群的其他节点。这种已经被删除,但是仍残留的记录叫做zombie。
为了防止出现zombies,Cassandra给每个墓碑一个宽限期(grace period)。grace period的目的就是给没有应答的节点时间去recover和正常处理墓碑。When multiple replica answers are part of a read request, and those responses differ, then whichever values are most recent take precedence. For example:如果一个节点有一个墓碑数据,但另一个节点有关于这一行更新的操作,那么最终的结果是返回更新的操作;如果一个节点有一个墓碑数据,但另一个节点有关于这一行更旧的value,那么最终的结果是返回墓碑数据(也就是该数据被删除了,数据不存在)。如果一个客户端在墓碑数据的grace period写入了一个新的更新。Cassandra会覆盖掉此墓碑。
When an unresponsive node recovers, Cassandra uses hinted handoff to replay the database mutations the node missed while it was down. Cassandra does not replay a mutation for a tombstoned record during its grace period. But if the node does not recover until after the grace period ends, Cassandra may miss the deletion.
当一个无应答的节点恢复过来,Cassandra使用hinted handoff来replay 恢复节点down期间错过的mutations。Cassandra不会去replay处于grace period的墓碑的数据(也就是说,如果这个墓碑还在宽限期,那么这个recover的节点就不要这个墓碑数据了,因为它被删除了,不是指不要这个墓碑标记,是不要这个数据!!)。但是如果在grace period结束后,节点还没有恢复,则Cassandra可能会丢失这个数据的墓碑标记,因此错过这个数据的删除操作(就会导致删除的数据重现)。
也就是说 正常的节点,一开始写入了一个数据A,然后删除数据A,也就是在commmit log和memtable中都写入了一个insert A 和deleteA的record,但是另一个节点宕机了,只接收到insert A的record,然后正常的节点 把memtable dump成sstable,然后sstable compaction后 就将A数据删除了,但是replay的时候不是replay commitlog吗? 那么deleteA这个record应该还在的吧?commitlog 难道也要GC?
当墓碑的grace period结束后,Cassandra会在compaction阶段删除墓碑。
一个墓碑的grace period是通过设置gc_grace_seconds属性来设置的。默认值是86400秒(10天)。每个表这个属性可以单独设置。
More about Cassandra deletes
l 墓碑数据过期的日期/时间是它创建的日期/时间加上表的gc_grace_seconds参数。
l Cassandra也支持批量插入和更新。这个过程(和普通的插入和更新相似)也可能会产生zombie。Cassandra不会在墓碑数据的grace period期间 replay batched mutations。
l 在一个单节点的集群中,你可以设置gc_grace_seconds的值为0
l 为了完全防止zombie数据的再现,可以在节点恢复后,在节点上运行nodetool repair命令,必须是在每个表的gc_grace_seconds期间内执行,不能超期。
l 如果表中的所有数据在创建时都被赋予TTL,并且所有数据都允许过期自动删除,而不是手动删除,则不需要定期为该表运行nodetool repair命令。
l 如果使用SizeTieredCompactionStrategy或DateTieredCompactionStrategy,则可以通过手动触发compaction来立即删除墓碑。
警告:如果使用force compaction,Cassandra可能会从所有数据中创建一个非常大的SSTable。 因此在很长一段时间内,都不会触发下一次compaction,因此在下次compaction发生前,这次force compaction过程中产生的SSTable中的数据会变得非常陈旧。
l 可以为整张表设置default_time_to_live属性。标有常规TTL的列和行如上所述进行处理;但是当记录超过表级TTL时,Cassandra会立即删除它,而不会标记为墓碑然后再compaction。
l 可以通过DROP KEYSPACE和DROP TABLE语句立即删除数据。