暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

图文解读MatrixDB自研存储引擎

yMatrix 2021-06-24
2152

存储引擎是数据库核心组件之一,本文将向读者介绍MatrixDB支持的存储引擎及其内部存储结构和对比。


        1. 数据存储方式       


在介绍存储引擎之前,先介绍一下数据存储常用的几种方式:
  1. 行存
  2. 列存
  3. 行列混存


1.1 行存
如下图所示,数据按行存储。同一行中不同的列存储在一起:

1.2 列存
与行存相对应,数据按列存储。不同行中相同的列存储在一起:

1.3 行列混存
行列混存兼具了行存和列存的特点,按照行分了若干组,每个组中按列存储: 

        2. MatrixDB存储引擎       


MatrixDB提供了Heap和Mars两种时序存储引擎,分别支持行存和行列混存两种存储方式。

2.1 Heap

Heap存储引擎是行存方式,是MatrixDB建表时使用的默认存储引擎。
如下图所示,每个Heap表对应一个数据文件(上限1G,超过会增加),数据文件由Block构成,每个Block内存储了表中的多个行:
Heap表数据存储并无顺序,按照插入先后顺序存储。所以,对Heap表基于键值快速检索要额外建立索引,最常用的就是BTree索引。
如下图所示,BTree索引是一颗高度平衡树,数据索引存储在叶子节点中,指向数据所在行:


2.2 Mars

Mars存储引擎是MatrixDB自研的存储引擎,是行列混存。
Mars引擎包含两个重要的概念
  1. Row Group
  2. Footer


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,存储在独立的文件中:

        3. Mars引擎查询执行优化       


从前面了解到Mars引擎的数据存储有如下特征:
  1. 数据是按照设备id和时间戳有序存储
  2. 每个Row Group内按列存储
  3. 每个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 等值条件

SQL样例:
SELECT * FROM table WHERE c1 = v0; 
当目标值v0在最小值和最大值范围内,则对Row Group进行扫描,否则跳过:

3.2.2 大于或大于等于条件

SQL样例:
SELECT * FROM table WHERE c1 >= v0; 
当比较值v0小于等于最大值max的时候,需要进行扫描;否则跳过:

3.2.3 小于或小于等于条件

SQL样例:
SELECT * FROM table WHERE c1 <= v0; 
当比较值v0大于等于最小值min的时候,需要进行扫描;否则跳过:
注意:并不是所有的条件都能利用扫描裁剪,比如不等于条件就无法使用。


3.3 定制聚集扫描

由于每个Row Group对应的元信息Footer中已经包含了MIN,MAX,SUM,COUNT聚集结果,所以对于整个Row Group都满足扫描条件的聚集查询,则可以直接使用Footer中的聚集结果,而跳过对原始数据的扫描,从而提高性能:
SQL样例:
SELECT COUNT(*) FROM table;
如下图,对比新旧两种聚集计划可以看出,新的计划省掉了Partial Aggregate和Seq Scan节点,被替换成直接从Footer取结果的Custom Scan:


3.4 定制排序扫描

因为时序数据在灌入Mars表的时候是按照tag_id和时间戳排序的。所以,如果查询也是ORDER BY tag_id, time的话,则不需要对扫描结果做额外排序,直接使用即可。
SQL样例:
SELECT * FROM table ORDER BY tag_id, time;
执行计划前后对比如下图:

        4. 时序场景存储引擎搭配方案       


在时序场景中,既要做海量数据接入,又要做高效查询,所以Heap和Mars两种引擎要搭配使用。推荐做法是:使用Heap接入实时热数据,当热数据时间窗口过后,将Heap数据排序并导入到Mars引擎。具体使用方法请参考文档“冷热分级存储”。
该方案结合了两种引擎的特征,优势如下:
  1. Heap可以建立唯一索引,方便热数据去重
  2. 如果热数据有错误,Heap可以直接做更新和删除
  3. 将Heap数据排序后导入Mars可以有效解决设备发送数据乱序问题
  4. 冷数据不需要更新,存储在Mars中查询更高效
注意:数据从Heap表导入Mars表的时候要按照设备id和时间戳做排序。

        5. 总结       


对比MatrixDB中两种引擎可以看出:
  1. Heap表按行集中存储,相同的行存在一个Block中,数据无序存储,无预聚集。按键值检索需要额外建立索引。
  2. Mars表按列集中存储,相同的列存在一个Page里,数据有序存储。通过Footer来做稀疏索引和预聚集,存储空间更小,基于设备id和时间戳的检索速度快。

所以,Mars引擎更适合基于时间戳的分析查询,因为:
  1. 数据分析通常针对某个指标,即某个列,并不需要所有列的信息。按列存储可以在物理上只读取需要的列,减少不必要IO操作
  2. 相同的列因为类型相同,更容易做压缩
  3. Footer中包含了Row Group的聚集信息,在做更大范围的聚集操作时则可以减少很多计算量;做条件扫描也可以做裁剪
  4. 数据有序存储,对于需要按照排序键排序的查询,可以降低成本

文章转载自yMatrix,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论