Cassandra源码阅读(3)-Memtable Flush

Cassandra源码阅读(3)-Memtable Flush

本文介绍Cassandra的memtable flush到sstable的过程,重点关注下面两个问题

  1. 什么时候memtable会被flush到sstable
  2. 如果刷到sstable

何时触发

  1. 在Memtable初始化的时候,里面有一个memtable_pool,这个pool初始化的时候有一个cleaner参数,用的是ColumnFamilyStore.flushLargestColumnFamily这个方法返回的runnable。

  2. 上面说的这个runnable被包在一个InfiniteLoopExecutor里面,外部通过调用对应的trigger方法来触发Flush(MemtableCleanerThread.trigger()).
    通常是在外部subpool.allocate或者其他需要新开空间的情况下会调用maybeClean(MemtablePool.SubPool.maybeClean()),里面会调用trigger方法。

  3. 上面1说的ColumnFamilyStore.flushLargestColumnFamily这个方法返回的runnable里面具体的逻辑是在所有columnFamilyStore中找到最大的,然后调用memtable.cfs.switchMemtableIfCurrent方法来进行刷sstable。

如何刷

上面介绍了时候刷盘,现在介绍下刷盘是如何做的。

接着上面的说的switchMemtableIfCurrent,这个方法内部会调用columnFamilyStore.flush方法。这个方法处理具体的flush逻辑,具体逻辑如下

  1. memtable.createFlushRunnable中,按照当前节点所持有的token(tokenRing中的token,cassandra是一个有虚拟节点的hash环结构,每个node都会分到对应范围的token)和配置中的dataDirectroy数量(cassandra可以配置多个data文件夹,如果有多个,每个文件夹负责一部分token,具体的分配逻辑在DiskBoundaryManager.getDiskBoundaries(ColumnFamilyStore)里面)来创建FlushRunnable.

  2. ColumnFamilyStore.perDiskflushExecutors执行上面的flushRunnable(这里的perDiskflushExecutors是一个数组,数组里面的每一个executor负责刷一个dataDirectory)。flushRunnable的call方法会调用 FlushRunnable.writeSortedContents方法,该方法遍历要flush的memtable中的partition,然后调用 writer.append( partition.unfilteredIterator()),也就是把对应partition的信息写到sstable中。这个writer是sstable.multiwriter里面包了一个Bigtablewriter,最后调用到了bigtableWriter.append上面去。

  3. 具体序列化并写入文件的过程在buildRowIndex里面完成。BigtableWriter里面包了一个sequentialWriter,这个writer内部包了一个filechannel就是对应磁盘上的文件。具体的文件名则是 ${version}-${generation}-big-data.db . version是跟着cassandra的版本走的,具体代码在(BigFormat.BigVersion).