Cassandra如何决定flush memtable的大小

一个多月没有更新博客了。我在积累,频繁更新的话,就没有东西写了-_-。不过还是要定期的总结,以备忘,以分享,以交流,以提高。

这篇博客说的问题是Cassandra在memtable flush为sstable的时候,大小是如何计算的,以及flush的阈值是如何确定的。这个过程是动态的,并不是设定某个内存的大小,或者qps多少的时候,进行flush。一方面计算过程可能并不那么直接,另一方面1.1.3版本之前,这里隐藏了比较严重的bug:

  1. 可能会造成内存溢出(很严重)
  2. 可能会造成L0层的sstable文件很小(长期来看,也比较严重,严重影响读性能)

可能会有以下的日志:

  1. WARN [MemoryMeter:1] 2012-09-04 16:04:50,405 Memtable.java (line 181) setting live ratio to maximum of 64 instead of Infinity
  2.  INFO [MemoryMeter:1] 2012-09-04 16:04:50,393 Memtable.java (line 186) CFS(Keyspace='YourKeyspace', ColumnFamily='YourCF') liveRatio is 64.0 (just-counted was 64.0).  calculation took 96ms for 0 columns

这个是基于1.0.10版本Cassandra的。造成这个现象的具体原因是insert,delete等操作变化过于剧烈。上面的第二个问题,我亲身遇到过。主要场景是,开始大批量写入,然后大批量删除。造成两次flush之间只有删除操作,或者大量的都是删除,只有少数的insert。此时,live ratio就会计算为64,导致非常小的sstable大小。 下面具体结合代码分析一下什么是live ratio,如何计算,对memtable大小的影响。最关键的三个类:

  1. MeteredFlusher:在ColumnFamilyStore中启动,定期执行
  2. Memtable:包含memtable实际操作的代码,flush等
  3. ColumnFamilyStore:代表一个column family综合管理memtable,sstable,Compaction等。
live ratio表示数据所占内存的总大小和实际数据大小的比。很多人会对这个地方不理解。细说一点,为什么要有live ratio呢?Cassandra是java实现的,相信很多人都知道java是很耗内存的。被称为fat java。就是除了数据本身占用的空间意外,还存在大量的头信息,类型数据本身所占用的空间,也不可忽视。要考虑内容的使用情况,就应该是数据的实际大小*live ratio。Cassandra中,正是如此计算的。数据的实际大小是Memtable类中的currentThroughput变量表示的。这个变量的计算方法从resolve方法中,可以看到,对于批量删除操作而言,currentThroughput这个值是0,也就是为什么上面的日志中,会出现Infinity。在1.0.10版本之前,Cassandra的作者还限定了live ratio的范围,[0, 64] 。最后会和上次计算的live ratio进行比较,取最大的。这里就是一个bug,因为当live ratio达到64时,以后无论当前计算的live ratio如何小,max之后,都是64。所以一旦出现一个比较大的值,那么flushed sstable大小会一直很小。
说完live ratio,应该说MeteredFlusher类,和这个类相关的一个参数是
memtable_total_space_in_mb
这个参数表示memtable可占内存的总大小。这个类的内容就比较清晰了,计算是否超过阈值,超了就flush。 另外:memtable 数据大小的计算公式如下:
(long) (currentThroughput.get() * cfs.liveRatio * 1.25);
很谨慎的一个公式,考虑的全面,尽量避免内存溢出。


对这个进行分析,也是以后分析写过程,改进写过程的基础。上面提高的问题,已经在1.1.3版本中基本修正了。 【完】 【后记】 我已经尽量不涉及太多公式了,写完了,自己还是觉得很枯燥,就写给遇到这个bug的同学吧。另外,Cassandra内存管理,有一些比较先进的理念。以后可以多多总结。欢迎交流。

posted on 2012-09-11 11:39  sing1ee  阅读(1242)  评论(0编辑  收藏  举报