OceanBase数据库采用了基于 LSM Tree 结构作为数据库的存储引擎,数据被分为基线数据(SSTable)和增量数据(MemTable)两部分,基线数据被保存在磁盘中,当需要读取的时候会被加载到数据库的缓存中,当数据被不断插入(或者修改时)在内存中缓存增量数据,当增量数据达到一定阀值时,就把增量数据刷新到磁盘上,当磁盘上的增量数据达到一定阀值时再把磁盘上的增量数据和基线数据进行合并。
对于 LSM Tree 结构,如果保存多个层次的 MemTable 的话,会带来很大的空间存储问题,OceanBase 对 LSM Tree 结构进行了简化,只保留了C0层和C1层(参考上期有问必答内容),也就是说,内存中的增量数据会被以MemTable的方式保存在磁盘中,这个过程被称之为转储(compaction),当转储了一定的次数之后,就需要把磁盘上的MemTable与基线数据进行合并(merge)。以下是转储与合并的详细解释:
转储:由于内存中对数据的修改会持续发生,所以在内存中的 MemTable 就会越来越多,为了释放内存空间,OceanBase 会定义一个 MemTable 占用内存比例的阀值,当到达这个阀值的时候,就要把一些最旧的 MemTable 中的信息进行归并排序,并保存到磁盘上,形成C0级别的数据,这个过程称之为转储,OceanBase 把这个过程称之为转储(Minor Freeze)。
合并:合并操作(Major Freeze)是将动静态数据做归并,也就是产生新的C1层的数据,会比较费时。当转储产生的增量数据积累到一定程度时通过 Major Freeze 实现大版本的合并。由于在合并的过程中为了保证数据的一致性,就需要在合并的过程中暂停正在被合并的数据上的事务,这对性能来说是会有影响的,OceanBase 对合并操作进行了细化,分为增量合并,轮转合并和全量合并。
下面的表格描述了转储与合并的区别:
OceanBase同时也结合了传统的关系型数据库的特点,也存在数据库块的概念。在 OceanBase 中,数据文件分配空间的单位称为宏块(marco block),如果大家对Oracle比较熟悉的话,可以简单的认为宏块对应了Oracle中的extent;每个宏块又分成了若干个16k大小的微块,它是每次数据库IO的最小单位(相当于传统数据库的块),数据库中的各种数据就保存在微块当中。由于宏块的大小是2M,而且 OceanBase 采用了LSM Tree 结构来保存数据,数据是按照表的主键排序的。所以,OceanBase 的宏块是可以分裂的,而如果数据被删除了,相邻的宏块也可以进行合并
由于 SSTable 中的数据是基线数据,绝大部分情况下,这部分数据是静态的,所以 OceanBase 默认会对这些数据在进行合并时进行分析,并根据各个列的数据分布情况对数据进行编码,目前支持的编码方式有:字典编码、RLE编码、常量编码、差值编码、前缀编码、列间编码等。在对数据进行编码之后,再通过通用的压缩算法对数据进行压缩,就可以实现很好的数据压缩比,同时对于读取性能基本没有影响,而且使合并时的写入性能更好。下面的图片展示了使用字典方式对列rate_id进行编码的基本过程。