写在前面: 本文主要梳理了Cassandra读数据的过程,列举了几个常用参数,尝试解释了在读取操作中内存变化的原因。但是关于内存变化还是有疑点,本文也会抛出,待阅读源码后再来补充。文章参考:docs.datastax.com/en/cassandr…
抛出问题
测试服务器内存8G,测试数据5000w+个partition,总数据量13G+,Cassandra空闲状态如下:
开启压测后内存到达峰值:
内存回落:
由于事先了解到Cassandra有memtable_offheap_space_in_mb和memtable_cleanup_threshold两个配置,分别表示堆内内存和堆外内存。这两个配置分别默认使用系统内存1/4,也就是差不多2G。初始状态内存占用2G左右,峰值占用4G左右。这样看来,内存的变化有可能是出现了GC,或是有缓存策略。
Cassandra写流程
为了满足读取功能,Cassandra必须在数据经常变动的memtable和多个可能有数据的SSTable中综合地获取结果。从memtable到SSTable,获取数据的步骤如下:
- 检查
memtable; - 如果开启
row cache,则检查; - 经过Bloom过滤器;
- 如果开启
partition key cache,则检查。// TODO 拓展Cassandra的Primary key、Partition key和Cluster key的概念; - 若partition key从
partition key cache中找到,则直接进入compression offsets;若没找到,则先经过partition summary再经过partition index中寻找; - 用压缩数据的偏移量定位磁盘上的数据;
- 从磁盘上的
SSTable中获取数据;
下图为官方的读请求数据流:
读取请求数据流中每个阶段的详细解释
Memtable
如果memtable具有所需的分区数据,则读取该数据,然后将其鱼表中的数据合并。并按照后续步骤访问表数据。
// TODO How is data maintained?
Row Cache
row cache以LRU的方式来存储数据,并且在Cassandra 2.2和以后的版本中数据存储在堆外内存。row cache的大小以及行数由以下两个配置来管理:row_cache_size_in_mb和row_cache_keys_to_save。该配置在后续有用到,所以此处提一下。
Cassandra在读操作密集型的情况下,使用row cache可以包揽95%的负载,是个提高读性能的利器。但是在写操作密集型的情况下不建议写入,因为row cache会频繁更新。
Bloom Filter
bloom filter用于发现哪些表有请求分区数据,当然它并不能表示表的所有数据,这与bloom filter的算法本身有关。该数据存储在堆外内存。
每十亿跳数据,bloom filter只需要使用大约1到2GB的内存空间。极端情况下,每行作为一个分区,一台服务器也能轻松地提供十亿条数据的读取服务。
Partition Key Cache
partition key cache在堆外内存存储分区key。若该缓存命中后,可以直接去查询压缩数据的偏移量。若每命重,则需要通过partition summary和partition index的两个步骤来处理。由于partition index存储在磁盘,所以该缓存的设置还是能极大程度提高效率的。
Partition Summary
partition summary也存储在堆外内存。分区索引包含所有的分区key,而分区摘要默认每128个key采样一次,并映射该key的索引对应的文件位置。这样一个请求进来,虽然不知道具体的分区索引的精确位置,但是能缩短数据位置的扫描时间。在找到范围之后再进行索引的搜索。
Partition Index
partition index存储在磁盘上,并存储了映射到其偏移量的所有分区key的索引。数据请求进来之后,会顺序查找和读取。
Compression offset map
compression offset map存储指向磁盘上具体分区数据的确切位置的指针。它存储在堆外内存中,由partition key cache和partition index来访问。一旦compression offset map识别出磁盘位置,就从目标表中获取压缩分区数据。
compression offset map每TB压缩1至3GB。压缩数据越多,压缩快的数据量越多,压缩偏移量也就越大。虽然压缩功能会消耗cpu资源,但是在读取密集的情况下是推荐开启的(虽然默认就是开启的)。
未解决的问题
经过上述读取操作的解读之后,接下来查看一下内存使用情况。使用命令./bin/nodetool cfstats keyspace.table可以得到如下结果:
可以看到无论是bloom filter还是partition index甚至是compression offset map占用内存并不是特别多。很容易想到是不是数据都在row cache中?并且row cache也有数据清理机制,和内存波动很像。但是令我奔溃的是,配置row cache大小的配置row_cache_size_in_mb默认为0,也就是说row cache默认不开启。为啥内存会有这么大的反复波动?这块后续阅读源码后补充。