存储引擎是数据库核心组件之一,本文将向读者介绍MatrixDB支持的存储引擎及其内部存储结构和对比。
在介绍存储引擎之前,先介绍一下数据存储常用的几种方式:
如下图所示,数据按行存储。同一行中不同的列存储在一起:与行存相对应,数据按列存储。不同行中相同的列存储在一起:行列混存兼具了行存和列存的特点,按照行分了若干组,每个组中按列存储:
MatrixDB提供了Heap和Mars两种时序存储引擎,分别支持行存和行列混存两种存储方式。2.1 Heap
Heap存储引擎是行存方式,是MatrixDB建表时使用的默认存储引擎。如下图所示,每个Heap表对应一个数据文件(上限1G,超过会增加),数据文件由Block构成,每个Block内存储了表中的多个行:Heap表数据存储并无顺序,按照插入先后顺序存储。所以,对Heap表基于键值快速检索要额外建立索引,最常用的就是BTree索引。如下图所示,BTree索引是一颗高度平衡树,数据索引存储在叶子节点中,指向数据所在行:
2.2 Mars
Mars存储引擎是MatrixDB自研的存储引擎,是行列混存。
2.2.1 Row Group
如图所示,Mars引擎中数据是按Row Group组织起来的,每个Row Group包含了一组有序存储的行。Row Group内部按列分页存储:那么一个Row Group中要存储多少行呢?这取决于建表时定义的time bucket。作为时序数据库,所有数据都是来自设备,会包含设备id(tag_id)和时间戳。所以,当新数据时间戳超过time bucket阈值或设备id变化的时候,就会创建新的Row Group。Row Group内部数据有序存放。
2.2.2 Footer
Footer存储了Row Group的元数据,如:行数、累计和、最大值、最小值,相当于Row Group的稀疏索引。在MatrixDB中每个Row Group对应一个Footer,存储在独立的文件中:- 每个Row Group在Footer中都包含COUNT,SUM,MIN,MAX聚集结果
3.1 列裁剪
如果查询中只指定了部分列,则可以使用列裁剪,即在数据扫描时跳过不需要的页。如下图所示,当只查询Column1的时候,做Scan只读取Row Group中Column1的相关页即可,大大减少了IO操作,这也是列存的好处。
3.2 扫描裁剪
因为每个Row Group的MIN,MAX都已知晓,所以在做条件扫描的时候就可以根据扫描条件和每个Row Group的最大值、最小值来决定是否需要进行裁剪。3.2.1 等值条件
SELECT * FROM table WHERE c1 = v0; 当目标值v0在最小值和最大值范围内,则对Row Group进行扫描,否则跳过:3.2.2 大于或大于等于条件
SELECT * FROM table WHERE c1 >= v0; 当比较值v0小于等于最大值max的时候,需要进行扫描;否则跳过:3.2.3 小于或小于等于条件
SELECT * FROM table WHERE c1 <= v0; 当比较值v0大于等于最小值min的时候,需要进行扫描;否则跳过:注意:并不是所有的条件都能利用扫描裁剪,比如不等于条件就无法使用。
3.3 定制聚集扫描
由于每个Row Group对应的元信息Footer中已经包含了MIN,MAX,SUM,COUNT聚集结果,所以对于整个Row Group都满足扫描条件的聚集查询,则可以直接使用Footer中的聚集结果,而跳过对原始数据的扫描,从而提高性能:SELECT COUNT(*) FROM table;如下图,对比新旧两种聚集计划可以看出,新的计划省掉了Partial Aggregate和Seq Scan节点,被替换成直接从Footer取结果的Custom Scan:
3.4 定制排序扫描
因为时序数据在灌入Mars表的时候是按照tag_id和时间戳排序的。所以,如果查询也是ORDER BY tag_id, time的话,则不需要对扫描结果做额外排序,直接使用即可。SELECT * FROM table ORDER BY tag_id, time;在时序场景中,既要做海量数据接入,又要做高效查询,所以Heap和Mars两种引擎要搭配使用。推荐做法是:使用Heap接入实时热数据,当热数据时间窗口过后,将Heap数据排序并导入到Mars引擎。具体使用方法请参考文档“冷热分级存储”。- 将Heap数据排序后导入Mars可以有效解决设备发送数据乱序问题
注意:数据从Heap表导入Mars表的时候要按照设备id和时间戳做排序。- Heap表按行集中存储,相同的行存在一个Block中,数据无序存储,无预聚集。按键值检索需要额外建立索引。
- Mars表按列集中存储,相同的列存在一个Page里,数据有序存储。通过Footer来做稀疏索引和预聚集,存储空间更小,基于设备id和时间戳的检索速度快。
所以,Mars引擎更适合基于时间戳的分析查询,因为:- 数据分析通常针对某个指标,即某个列,并不需要所有列的信息。按列存储可以在物理上只读取需要的列,减少不必要IO操作
- Footer中包含了Row Group的聚集信息,在做更大范围的聚集操作时则可以减少很多计算量;做条件扫描也可以做裁剪
- 数据有序存储,对于需要按照排序键排序的查询,可以降低成本