统计了下近几年在社区和微信群里面,Apache Cassandra用户问到最多的问题。一个是分区问题,另一个就是墓碑问题。的确,墓碑可能是 Apache Cassandra更令人沮丧的问题之一。当在短时间内发生大量删除时,可能会出现逻辑删除问题,从而导致查询速度变慢和磁盘膨胀,然后让你无所适从。本文将讨论应对墓碑的一些小技巧,让你觉得墓碑并不是那么可怕。

Apache Cassandra 中的墓碑是在删除值时创建的标记。它们的目的是表明任何先前的值都已被删除。当发生读取时,它会从可能包含数据的节点上的排序字符串表 (SSTable) 文件中提取数据。返回请求键的所有先前值,按写入时间排序。因此,如果一个特定的值被多次写入和删除,可能会有许多墓碑和过时的值需要读取和协调。墓碑会增加查询的延迟,并且会返回大量您不想要的“幽灵数据”(已删除的值)。因此,墓碑是与 Cassandra 合作的一些更令人沮丧的方面。
墓碑在系统中存在一定时间后,根据表配置的gc_grace_seconds(默认为 10 天),在该表的下一个压缩周期中将其删除。
面对这个问题,Datastax公司根据全球数千家客户的服务经验,总结了以下技巧。
避免创建墓碑
当然,处理墓碑的最佳方法是一开始就不要创建它们。有几种方法可以实现这一点,其中大多数发生在数据模型中。
没有删除
避免墓碑的最简单方法是永远不要执行一个保证创建它们的操作——删除数据。当然,这并不总是可能的,并且肯定会通过用例适用。但总的来说,如果您有一个不需要定期删除数据的数据模型,您将避免导致墓碑出现的大部分痛苦。
不写 NULL 值
将 NULL 值写入 Cassandra 时,它只会删除相应的列。建议升级driver,当写入NULL 的时候配置成 Unset。
减轻墓碑的影响
在某些用例中,删除操作及其产生的墓碑是不可避免的。在这种情况下,最好的前进路径是调整数据模型和使用模式,以便返回尽可能少的墓碑。
集合操作
集合(集合、列表和映射)的实现是对Cassandra 数据建模范式的一个很好的补充。但重要的是要记住,频繁添加、删除或更新集合项会导致生成墓碑。使用集合时,您会通过尽可能少地更改其内容来获得成功。
使用 TTL
在表上设置生存时间 (TTL) 是防止数据集大小失控的好方法。但重要的是要记住,一旦 TTL 激活并删除有问题的数据,它就会创建一个墓碑。TTL 最适合与更关心最近数据的时间序列模型一起使用。
CREATE TABLE messages (day text,message_time timestamp,message_text text,PRIMARY KEY (day, message_time)) WITH CLUSTERING ORDER BY (message_time DESC)AND default_time_to_live = 2592000};
在上面显示的表定义中,消息按天分区。message_time在每个分区中,消息按降序“聚集” 。Cassandra 使用集群键来强制执行磁盘排序顺序。这样,可以快速从磁盘顺序读取数据。一个月default_time_to_live后,旧邮件将被删除。
这里的好处是,如果应用程序只关心查询最新数据,它永远不应该查询墓碑。这很重要,因为当作为结果集的一部分返回时,墓碑大多是有问题的。如果从不询问它们,它们引起问题的可能性要小得多。
压实策略选择
Apache Cassandra 的默认压缩策略是SizeTieredCompactionStrategy. 它是一个很好的默认值,因为它适用于许多用例和访问模式。在处理基于日志的时间序列用例时,TimeWindowCompactionStrategy 是一个不错的选择。这个想法是根据特定的时间组件或“窗口”存储数据。这是使用 TTL 的一个很好的压缩策略,因为 SSTable 文件本身是根据指定的时间窗口存储的。这允许压缩过程删除旧数据的整个文件,这比在将数据与其他文件合并时协调单个墓碑更有效。另一种流行的替代方法SizeTieredCompactionStrategy是LeveledCompactionStrategy。LeveledCompactionStrategy 对读密集型的业务是比较有优势的,这种方式的优点是同一层的块之间没有重复数据,带来的好处就是在合并操作的时候,并不需要扫描一层中的所有数据块。合并的开销变小了。同时它的压缩条件更容易触发,所以它往往更容易删除墓碑。
注意:更改表的压缩策略的决定不能掉以轻心。它本身并不能减轻墓碑的影响。上面提到的压缩策略是为特定的用例和访问模式设计的,不遵守这些模式可能会导致额外的问题。
移除墓碑
假设您正处于一个表有大量墓碑的位置。查询明显变慢,偶尔TombstoneOverwhelmingException会返回。在这一点上,你能做什么?
Compaction options
默认SizeTieredCompactionStrategy确实提供了一些选项来帮助清理墓碑。最有效的是 tombstone_threshold,它以百分比为值,例如:
'tombstone_threshold':'0.3'
在这种情况下,当文件中过期数据的百分比超过 30% 时,将启动单 SSTable 压缩。但是,过期数据百分比不考虑gc_grace_seconds设置,只会删除符合条件的过期数据。
nodetool garbagecollect
可以通过调用以下命令使用单表压缩来删除墓碑:
nodetool garbagecollect <keyspace> <table>
此命令可用于回收一些空间并清除过期数据。请务必记住,此命令也受gc_grace_seconds设置约束。
注意:注意不要将garbagecollect关键字与管理 Java 堆内内存使用的垃圾收集进程混淆。
降低 gc_grace_seconds
一种更快地循环墓碑的明显方法是降低表的值gc_grace_seconds。这具有在较短的时间内将墓碑标记为有资格收集的效果。默认情况下,gc_grace_seconds设置为 10 天(864,000 秒)。
对此设置要非常小心。10 天等待期的想法是与每周运行维修操作的建议相吻合。这使墓碑有足够的时间复制到负责特定的已删除副本的节点。如果这个周期设置得太低,很可能不是所有的副本都会收到墓碑。这是数据复活的常见原因。
注意:永远不要设置gc_grace_seconds为零!在墓碑有时间正确复制之前,没有其他方法可以防止压缩清理墓碑。
总结
有效地处理墓碑需要预先计划并了解(两者)业务用例以及 Cassandra 如何在内部处理删除。毕竟,不受墓碑问题影响的最佳方法是一开始就不要创建它们。手动移除有问题的墓碑可能是一项具有挑战性的任务,而且无法保证成功。压缩过程的有机运行是删除过期数据(包括墓碑)的最佳方法。如果可能的话,让 Cassandra 自己处理这件事就好,尽量减少人工干预。
确保应用代码没有写入 NULL 值。
按时间戳(按降序)对表进行聚类可以更轻松地避免查询过期或 TTL 数据。
根据它与应用程序的数据访问模式的匹配程度来选择压缩策略,而不是根据它处理墓碑的方式。
设置表格是tombstone_threshold保持符合条件的墓碑倒计时的有效方法。
尽量避免手动运行压缩任务。
最后来一个本期直播预告(就在5月19号),下期直播我们将聊聊Cassandra的健康检查,欢迎扫描进入直播交流群:





