和关系数据库一样,Cassandra在写数据之前,也需要先记录日志,称之为commitlog,然后数据才会写入到Column Family对应的Memtable中,并且Memtable中的内容是按照key排序好的。Memtable是一种内存结构,满足一定条件后批量刷新到磁盘上,存储为SSTable。这种机制,相当于缓存写回机制(Write-back Cache),优势在于将随机IO写变成顺序IO写,降低大量的写操作对于存储系统的压力。SSTable一旦完成写入,就不可变更,只能读取。下一次Memtable需要刷新到一个新的SSTable文件中。所以对于Cassandra来说,可以认为只有顺序写,没有随机写操作。
因为SSTable数据不可更新,可能导致同一个Column Family的数据存储在多个SSTable中,这时查询数据时,需要去合并读取Column Family所有的SSTable和Memtable,这样到一个Column Family的数量很大的时候,可能导致查询效率严重下降。因此需要有一种机制能快速定位查询的Key落在哪些SSTable中,而不需要去读取合并所有的SSTable。Cassandra采用的是Bloom Filter算法,通过多个hash函数将key映射到一个位图中,来快速判断这个key属于哪个SSTable。为了避免大量SSTable带来的性能影响,Cassandra也提供一种定期将多个SSTable合并成一个新的SSTable的机制,因为每个SSTable中的key都是已经排序好的,因此只需要做一次合并排序就可以完成该任务,代价还是可以接受的。
Cassandra 中的数据主要分为三种:
- CommitLog:Cassandra在写数据之前,也需要先记录日志,称之为commitlog,然后数据才会写入到Column Family对应的Memtable中,并且Memtable中的内容是按照key排序好的。commitlog主要记录客户端提交过来的数据以及操作。这个数据将被持久化到磁盘中,以便数据没有被持久化到磁盘时可以用来恢复。
Commitlog是server级别的,不是Column Family级别的 。 每个Commitlog文件的大小是固定的,称之为一个Commitlog Segment,当一个Commitlog文件写满以后,会新建一个的文件。当旧的Commitlog文件不再需要时,会自动清除。
每个Commitlog文件(Segment)都有一个固定大小(大小根据Column Family的数目而定)的CommitlogHeader 结 构,其中有两个重要的数组,每一个Column Family在这两个数组中都存在一个对应的元素。其中一个是位图数组(BitSet dirty ),如果Column Family对应的Memtable中有脏数据,则置为1,否则为0,这在恢复的时候可以指出哪些Column Family是需要利用Commitlog进行恢复的。另外一个是整数数组(int[] lastFlushedAt ), 保存的是Column Family在上一次Flush时日志的偏移位置,恢复时则可以从这个位置读取Commitlog记录。通过这两个数组结构,Cassandra可以在异 常重启服务的时候根据持久化的SSTable和Commitlog重构内存中Memtable的内容,也就是类似Oracle等关系型数据库的实例恢复。当Memtable flush到磁盘的SStable时,会将所有Commitlog文件的dirty数组对应的位清零,而在Commitlog达到大小限制创建新的文件 时,dirty数组会从上一个文件中继承过来。如果一个Commitlog文件的dirty数组全部被清零,则表示这个Commitlog在恢复的时候不 再需要,可以被清除。因此,在恢复的时候,所有的磁盘上存在的Commitlog文件都是需要的。 - Memtable:数据写入的第二个阶段,每一个columnfamily对应一个memtable。也就是每一张表对应一个。用户写的数据在内存中的形式,
- SSTable:这是磁盘存储数据的文件。每一个column family都会对应一个SSTable文件。这又分为 Data、Index 和 Filter 三种数据格式。其中Data.db文件是SSTable数据文件,SSTable是Sorted Strings Table的缩写,按照key排序后存储key/value键值字符串。index.db是索引文件,保存的是每个key在数据文件中的偏移位置,而Filter.db则是Bloom Filter算法生产的映射文件。