cassandra1.1.0中Compaction部分源代码解析——LeveledCompactionStrategy
来源:互联网 发布:vb代码大全 编辑:程序博客网 时间:2024/05/29 18:24
近两天事情有点小多,更新速度不太给力,当然这都是借口。Ok,开始LeveledCompactionStrategy的分析。
前篇博客中也提到Compaction启动是在cfs中进行的,其实流程就是调用相应Compaction的构造函数初始化对应的对象。具体构造函数的完成的任务如下:
1)调用父类的构造函数,生成一个延时启动的线程,该线程的功能是在启动5分钟之后调用CompactionManager.instance.submitBackground()方法,该方法的作用前一篇博客也讲到就是提交compaction任务,就是具体进行compaction的工作,也是整个compaction机制的核心。之所以延时启动这个线程,可能是因为compaction是一个非常耗资源的过程,在整个程序启动一段时间后再进行执行是比较合适的。
Runnable runnable = new Runnable() { public void run() { if (CompactionManager.instance.getActiveCompactions() == 0) { CompactionManager.instance.submitBackground(AbstractCompactionStrategy.this.cfs); } } }; StorageService.optionalTasks.schedule(runnable, 5 * 60, TimeUnit.SECONDS);
1)确定LeveledCompactionStrategy每个sstable的大小,因为LeveledCompactionStrategy中管理的每个sstable大小都是基本相同的(除去每次compaction的最后一个sstable,原因很明显)。
2)通过cfs.getDataTracker().subscribe(this);将自身传递给DataTracker,DataTracker是cfs具体管理数据的一个类,在后续的读写部分的源代码解析中也会详细的介绍。其实这个传递起到的主要作用是,cfs与compaction之间的一种信息交互手段,类似于回调函数。
3)创建manifest =LeveledManifest.create(cfs, this.maxSSTableSizeInMB); LeveledManifest其实就是扫描获取LeveledCompactionStrategy管理的sstable的一下元信息。这些元信息都存储在一个XXX.json的数据文件中,其实就是每个层次包含哪些sstable的信息。
那我们可以看到构造函数完成的工作还是非常多的,前三点工作介绍的已经比较清楚了,下面我们分析下第4点。因为LeveledManifest类是LeveledCompactionStrategy的一个很重要的辅助类。我们分析下在它的create函数中发生了那些剧情。
1)创建一个LeveledManifest对象。
LeveledManifest manifest = new LeveledManifest(cfs,maxSSTableSize);
LeveledManifest的构造函数主要完成generations和sstableGenerations这个数据结构的创建,从名字很容易看出两者的功能generations存储的每层存储的sstable的情况,而sstableGenerations存储的每个sstable对应的层次。
2)通过遍历管理的sstable,结合xxx.json确定sstable和层次之间的对应关系,具体确定是通过一个generation的成员变量,其实也就是我们能够看到的每个sstable的编号,这个编号是唯一的。
3)将不在xxx.json中的sstable放入到第0层,具体代码如下:
for (SSTableReader ssTableReader : cfs.getSSTables()) { if (manifest.levelOf(ssTableReader) < 0) manifest.add(ssTableReader); }
4)最后返回一个LeveledManifest对象。
下边我们来看compaction的核心部分,就是上文提到延时启动线程调用的部分CompactionManager.instance.submitBackground,当然在前一篇博文中也提到有三个部分可以调用该函数,也就是有三个入口可以触发compaction的过程。
submitBackground 函数是在CompactionManager.java中,
Runnable runnable = new WrappedRunnable() { protected void runMayThrow() throws IOException { compactionLock.readLock().lock(); try { AbstractCompactionStrategy strategy = cfs.getCompactionStrategy(); AbstractCompactionTask task = strategy.getNextBackgroundTask(getDefaultGcBefore(cfs)); if (task == null || !task.markSSTablesForCompaction()) return; try { task.execute(executor); } finally { task.unmarkSSTables(); } submitBackground(cfs); } finally { compactionLock.readLock().unlock(); } } }; return executor.submit(runnable);该函数主要工作就是创建一个线程类对象runnable,然后进行提交。这里executor类似于一个线程池,大小默认是节点的processor的数目。那么看上去LeveledCompactionStrategy的整个compaction过程像是一个多线程的过程。其实不然。LeveledCompactionStrategy采用了特殊的机制,确保这是但一线程在进行操作,这样的目的是保证LeveledCompactionStrategy的L0以上的层都能够是有序的这一特性。
具体实现实在getNextBackgroundTask()这个函数中,
LeveledCompactionTask currentTask = task.get(); if (currentTask != null && !currentTask.isDone()) { logger.debug("Compaction still in progress for {}", this); return null; }这段代码就保证了单线程的操作,currentTask.isDone()这函数中用到了java中CountDownLatch这个比较好用的线程同步手段,类似于join的功能。
具体获取要进行compaction的sstable的功能在manifest.getCompactionCandidates()的函数中实现。获取要compaction的sstable的顺序是从高层向底层检查的,之所以这样做的原因,在其对应代码的注释中有例子进行了解释,这里不再赘述。我们来看一下在每个层次是如何获取待compaction的sstable的,具体代码如下:
if (level == 0) { // because L0 files may overlap each other, we treat compactions there specially: // a L0 compaction also checks other L0 files for overlap. Set<SSTableReader> candidates = new HashSet<SSTableReader>(); // pick the oldest sstable from L0, and any that overlap with it List<SSTableReader> ageSortedSSTables = new ArrayList<SSTableReader>(generations[0]); Collections.sort(ageSortedSSTables, SSTable.maxTimestampComparator); List<SSTableReader> L0 = overlapping(ageSortedSSTables.get(0), generations[0]); L0 = L0.size() > MAX_COMPACTING_L0 ? L0.subList(0, MAX_COMPACTING_L0) : L0; // add the overlapping ones from L1 for (SSTableReader sstable : L0) candidates.addAll(overlapping(sstable, generations[1])); return candidates; } // for non-L0 compactions, pick up where we left off last time Collections.sort(generations[level], SSTable.sstableComparator); for (SSTableReader sstable : generations[level]) { // the first sstable that is > than the marked if (sstable.first.compareTo(lastCompactedKeys[level]) > 0) return overlapping(sstable, generations[(level + 1)]); } // or if there was no last time, start with the first sstable return overlapping(generations[level].get(0), generations[(level + 1)]);从代码中可以看出对于L0层的有特殊的处理方式,因为在LeveledCompactionStrategy中只有L0不同的sstable之间是有重复数据的,所以L0向L1合并时需要在L0找到与合并块有交集的块,这一点与其他层不同,还有一点就是需要限制L0向L1合并时sstable的数目,不能超过32块多余的将进行截断。
这些工作完成以后将形成一个任务(task),最终执行是调用该task的execute()方法。对与不同的compaction机制,这个执行方法都是相同的,具体方法在CompactionTask.java中的execute方法。
具体的compaction过程是:要进行compaction的sstable存储一个叫做tocompact的list中,合并需要先预留出于tocomapct所存储的所有sstable同样大小的磁盘空间,如果没有足够的磁盘空间此次任务将会失败(此处与SizetieredCompactionStrategy不同),原因还是LeveledCompactionStrategy层次之间不能存在重复数据的问题。因为LeveledCompactionStrategy规定每个sstable的大小,所以在compaction之后内容写出的时候,没超过设定的大小就新创建一个sstable。
ok,至此貌似整个compaction过程就结束了,其实细心的童鞋可能已经发现了问题,compaction新的sstable之后,老的sstable是不是该删除了,这些在cfs和compaction中都是怎么执行的呢。
cfs.replaceCompactedSSTables(toCompact, sstables, compactionType);
这个函数是cfs对新老sstable的处理。compaction部分对新老数据的处理是通过DataTracker的replaceCompactedSSTables函数进行的,具体是通过我们一开始提到的replaceCompactedSSTables获得的compactionStrategy的对象进行的,调用LeveledCompactionStrategy的subscriber.handleNotification(notification, this); 方法。
if (notification instanceof SSTableAddedNotification) { SSTableAddedNotification flushedNotification = (SSTableAddedNotification) notification; manifest.add(flushedNotification.added); } else if (notification instanceof SSTableListChangedNotification) { SSTableListChangedNotification listChangedNotification = (SSTableListChangedNotification) notification; switch (listChangedNotification.compactionType) { // Cleanup, scrub and updateSSTable shouldn't promote (see #3989) case CLEANUP: case SCRUB: case UPGRADE_SSTABLES: manifest.replace(listChangedNotification.removed, listChangedNotification.added); break; default: manifest.promote(listChangedNotification.removed, listChangedNotification.added); break; } }通过代码我们可以看出两种类型的notification ,一种是SSTableAddedNotification(这是在一个新的sstable加入的时候,那时也会调用该函数,通知compaction新的sstable加入了,可能需要进行compaction了),第二个就是SSTableListChangedNotification,这就是compaction结束之后需要处理的部分,可以看到有不少的分支,compaction过程需要走的是manifest.promote(listChangedNotification.removed, listChangedNotification.added)这条分支,在相应的函数中会删除老的sstable,添加新的sstable。
熬夜匆忙写完了,写的不是很仔细,可能里边也有不少的错别字,大家看后有什么不懂的地方,欢迎留言讨论,好了,回宿舍睡觉去。
- cassandra1.1.0中Compaction部分源代码解析——LeveledCompactionStrategy
- Cassandra1.1.0中Compaction部分源代码解析——准备篇
- Cassandra1.1.0中SizeTieredCompactionStrategy和LeveledCompactionStrategy的理解
- HBase Compaction解析
- hbase中compaction流程
- disksim raid 部分源代码解析
- HBase Minor&Major Compaction 解析
- 【转载】hbase中compaction流程
- 第二部分 关于Cassandra1.0.x节点间通讯《草稿》
- 第三部分 关于Cassandra1.0.x的数据分区
- 第四部分 为Cassandra1.0.x准备java运行环境
- 第五部分 安装Cassandra1.0.x的单实例模式
- leveldb代码阅读(14)——Level和Compaction
- Java源代码解析—Object
- Hbase中compaction的触发条件
- 部分源代码
- HBase写入性能及改造——multi-thread flush and compaction(续:详细测试数据)
- leveldb研究系列八——Ssttable文件的compaction和LRUcache
- Netfilter/Iptables学习(综合整理)
- jquery,淘宝商城放大镜效果
- Window 7 下 Mysql 安装笔记
- 20个Java人员非常有用的Java功能代码
- 关于序列的几个算法
- cassandra1.1.0中Compaction部分源代码解析——LeveledCompactionStrategy
- GNU ARM汇编--(五)中断汇编之嵌套中断处理
- android图片处理方法(不断收集中)
- qplot
- 机器学习在互联网应用面临的 10 大挑战
- 新闻发布系统
- Android 修改Bitmap 图片像素的信息 R G B 颜色值 详解
- 去看李记者,天津印象
- 上转型对象的成员变量和成员方法的调用