Cassandra1.1.0中SizeTieredCompactionStrategy和LeveledCompactionStrategy的理解

最近一段时间因为项目的需要对cassandra的两种compaction机制进行了比较深入的了解,后续的博客中会有会相应源代码的解析。
一、首选先来说一下刚发布的1.1.0在compaction这部分的变化,最表面上的变化就是某些函数的名称改变了,比如获取候选compaction集合的函数getBackgroundTasks更名为getNextBackgroudTask,当然主要的变化就出现在这个函数中。细心的朋友可能已经发现两个函数名的区别,貌似是将每次获取多个task改为每次获取一个task,恭喜打对了,实际上就是发生了这样的改变,所以说好的函数名还是很重要的。

了解这两种机制的朋友可能都知道LeveledCompactionStrategy原来就是每次只获取一个task,所以1.1.0对于LeveledCompactionStrategy机制getNextBackgroudTask函数基本没有改动,改动的地方就是对Task 的类型进行了细化,分为以下几种类型:

具体每种类型的作用在后续的代码分析中会介绍,这里只需要知道在compaction过程中的task默认的都是COMPACTION类型。SizeTieredCompactionStrategy有较大的改动,原先的SizeTieredCompactionStrategy每次会发挥若干个task(相似大小的sstable组成的bucket的数量),1.1.0中每次只返回sstable平均大小最小的一个task,这样的目的就是优先合并较小的sstable,这样的好处就是能够尽快的减少系统中sstable的数量。

二、两种compaction机制的理解


1 SizeTieredCompactionStrategy

1.1.0中SizeTieredCompactionStrategy具体实现方式是,将所有cfs的sstable按大小进行分组,将大小相似的sstable归为一组,这样就形成n个组(n>=1),再从这n个组中获取平均大小最小的一个组形成一个task进行compaction的操作。每组的大小必须在(4,32]的范围内,数量过多的组将进行截断。

1.1.0之后的改进有助于快速减少sstable的数量,减轻系统的负担,对于有大量写操作的系统来说这是一个很大的好处。
SizeTieredCompactionStrategy的一些考虑:

(1)1.1.0中SizeTieredCompactionStrategy还是没有对同时进行compaction的线程的数量进行限制,默认线程数量是机器processor的数量,而LeveledCompactionStrategy限定只是一个线程,实验测试发现SizeTieredCompactionStrategy比LeveledCompactionStrategy慢一些(在LeveledCompactionStrategy设定的sstable的大小为512M的时候,读写的差距都比较大)。考虑跟LeveledCompactionStrategy一样限定compaction线程的数目,测试效果,需要看写速度提升多少,对读性能的影响如何。因为如果如果有大块的sstable在进行compaction这个过程会比较长同时会一直占据I/O,导致合并小块sstable的线程也很慢,不利于快速减少系统中sstable的数量。

(2)使用一个unCompactionList存储未进行过compaction的sstables,给予unCompactionList更高的compaction优先级(具体实现可以先扫描该list),因为未进行过compaction的sstable一般比较小而且数量多,所以优先压缩“性价比”会高很多。

(3)当sstable变得比较大的时候(比如超过50G或者100G),考虑减小(4,32]这个区间,比如有10个50G的sstable在进行压缩,即使磁盘空间充足,这个compaction过程也会很漫长,如果此时有大批写入数据,很可能会导致写磁盘空间不足,而且这些新生成的sstable也不能快速被压缩,造成出现大量sstable的现象。

2 LeveledCompactionStrategy

LeveledCompactionStrategy具体的实现方式是,从最高的level上开始扫描是否需要进行compaction,如果有将该层的sstable形成一个task。优先对高层sstable优先进行compaction的好处是,能有效减少底层到高层compaction时合并的sstable的数目。有个特殊的地方是,level0层向level1层compaction的时候一个task最多包含32个sstable。

(1) 优先对高Level的sstable进行压缩,确实能减少参与compaction的sstable的数量,但是优先处理高层的sstable就会导致L0层的sstable的堆积,单点管理的sstable过多,影响读写的性能。目前考虑的一个优化方案是对L0的数据块进行单独的compaction,具体的实现方法可以很暴力(比如将L0中的n个sstable合并成一个,还放置在L0中,n个sstable的选择还是首先合并小的sstable,因为这样不但合并的速度快而且能够减少sstable的数量。L0合并后的sstable单独使用一个list进行管理,该list中的sstable优先放置到L1中。

(2) 同样可能存在磁盘空间不足的情况,比如Li的某个sstable A需要合并到Li+1,Li+1层现在有10000个sstable,最极端的情况是A与Li+1的所有sstable都有交集,那么一次需要对100001个sstable进行compaction,这样很容易就出先磁盘空间不足的情况。对于这个问题解决起来可能有些麻烦,最重要要的问题是必须保证有序,目前的一个想法是A先于Li+1的一部分sstable合并,合并之后的最后两个sstable(只所以选择最后两块是因为最多只可能有两个可能是一个sstable与后便sstable之间可能有交集)再与剩余的sstable中一部分进行合并,这样依次执行下去直到最后结束。
(3) 实验测试发现在设定sstable大小为10M和512M两种情况下写入的速度差别较大,前者写入速度5959ops/sec,后者是7519ops/sec。sstable设置的越大出现(2)中提到的问题的概率也就越高。

(4) 即使使用LeveledCompactionStrategy,在读取时也不能发挥这种层次的优势,比如某个读取的数据在5个level(2T数据最终会保存在5个level中)中都存在,现在读取方式还是需要5次seek,但是他们之间是存在新旧关系的,如果cfs的sstable也按层次管理那么只需要一次seek。当然cfs的sstable按层次管理就会造成不兼容其他compaction机制的问题,这个优化可以在确定使用LeveledCompactionStrategy时进行。

个人觉着LeveledCompactionStrategy十分的鸡肋,模仿了LevelDB的实现但是cfs的sstable有没按层次进行组织(当然这是为了兼容不同的compaction机制,作为一个通用的开软实现,可以理解,但是作为特殊的应用如果想使用LeveledCompactionStrategy,可以做相应的改动)只是一种compaction的实现。个人认为LeveledCompactionStrategy唯一的好处就是减少冗余数据,因为大于L0的所有层的sstable大小基本都是相同的(设定的sstable的大小)。





你可能感兴趣的:(cassandra)