作为一款高性能时序数据库,数据写入是最普遍场景,所以数据写入速度尤为重要。数据写入框架如下图所示: 设备产生实时数据,通过数据采集系统发送到 IoT 网关,再写入到 MatrixDB 中。 为此,MatrixDB 专门开发了MatrixGate 组件,来优化数据写性能。本文将详细讲解 MatrixGate 的实现方式。
1 MatrixDB 数据写入方式
首先,先介绍一下 MatrixDB 进行数据写入的两种方式:
1.1 直接INSERT
直接 INSERT,顾名思义,就是通过 INSERT INTO TABLE VALUES 语句插入数据,这是一种最基本最简单的方式,也是所有数据库都支持的方式。当今数据库系统在处理查询时都拥有高并发、低延迟的特性,尤其对于HTAP系统,这种方式在处理对吞吐量要求不是特别高的场景完全可以,比如两万行每秒或几十万数据点每秒。那么,当吞吐量逐渐加大,直接 INSERT 面临的问题是什么呢?这要从 MatrixDB 如何执行查询说起。 MatrixDB 是一款中心化分布式数据库系统,系统由 master 节点总控,负责客户端连接、查询执行路径生成、计划下发、结果汇总等。 对于一条 INSERT 语句,执行过程如下: 从上图执行INSERT流程可以看出:
- 直接 INSERT 意味着所有查询都要通过 master 做计划、下发。每个查询都经历相同的一整套流程,master 要消耗大量资源;
- 每条数据都要经历一整套从客户端到 master,再到 segment 的过程,延时也不可忽略。
对于直接 INSERT 有一个小小的优化方法,就是批量插入,即每条 INSERT 语句 VALUES 后面跟多个行。 批量 INSERT 可以减少 master 做查询计划的次数,但是每条数据还是要通过 master 节点,进行解析与分发,master仍然是瓶颈。
1.2 使用 MatrixGate
MatrixGate 是 MatrixDB 提供的一个数据写入组件,目的是提高数据写入性能。其设计理念就是充分发挥分布式数据系统的优势,将数据解析的过程下发到了 segment。具体使用方式,请参考数据加载服务器 MatrixGate。
1.3 两种写入方式对比
写入方式 | 优势 | 劣势 | 适用场景 |
---|---|---|---|
直接INSERT | 接口简单 | 吞吐量低 | 吞吐量低,几十万数据点/秒 |
MatrixGate | 吞吐量高,准实时 | 需要额外部署,有运维成本 | 吞吐量高,千万数据点/秒 |
2 MatrixGate 实现原理
本节详细介绍 MatrixGate 是如何做到高吞吐的。
2.1 外部表
首先要提到一种新的表类型,外部表。 与其他表类型数据存储在数据库内部不同,外部表数据存储在数据库外部,内部只存了表的元信息。在创建表的时候通过 LOCATION 关键字指定,可以是文件路径,PXF,S3 或 HTTP 接口。 当查询外部表时,会根据 LOCATION 的协议和路径去读取指定的内容,从 SQL 接口层面看起来和普通表没有任何区别,也可以与其他普通表做连接。 如下 SQL 语句,创建了一张名为 ext 的外部表,LOCATION 路径为file://mdw/home/mxadmin/e1.csv,格式为csv:
CREATE EXTERNAL TABLE ext(
c1 int,
c2 varchar(100)
)LOCATION ('file://mdw/home/mxadmin/e1.csv') format 'csv' (delimiter ',');
2.2 基于外部表并行导入数据
那么,如何将外部表数据导入到数据表呢? 通过执行如下语句: INSERT INTO dest SELECT * FROM external; 其中 dest 是目标数据表,external 是外部表,并且两个表的列数和类型必须相同。 如上 SQL 与普通的 INSERT INTO VALUES 最大的区别就是,数据解析与重分布执行在 segment 节点上,这样充分利用了分布式多机资源。 如上图所示,在实际运行过程中,会有两组 Gang,一组负责从 MatrixGate 拉取数据并分发到对应的 segment,另一组负责接收分发的数据然后存储到本地。 所以,master 节点并不处理实际数据,真正的解析与转发都是由 segment 进行,master 节点不再是瓶颈。
2.3 MatrixDB 数据写入流程
MatrixGate 利用了外部表导入数据的优势,实现了 HTTP 协议,和创建外部表、执行 INSERT SELECT 的过程。 从上图可以看出:
- MatrixGate 提供了两个数据接口
- 设备数据写入的推送接口
- 外部表数据获取的拉取接口
- 数据从推送到被 segmen t拉取中间无数据落盘,减少了 IO 次数
- MatrixGate 通过 master 执行 INSERT INTO dest SELECT * FROM external,实现数据从外部表到数据表的导入,即从设备到数据表
如要增加并发,在 MatrixGate 中启动多个任务并行执行 INSERT INTO dest SELECT * FROM external 即可。目前,MatrixGate 默认配置了 10 个并行任务。
2.4 准实时
最后,还有一个问题。那就是 INSERT INTO dest SELECT * FROM external 语句什么时候执行结束呢?
因为设备数据是一直在发送的,永不停歇,这就意味着 segment 一直能从 MatrixGate 拉到数据。如果插入语句一直执行不结束的话,数据就一直无法被其他会话看到;同时,事务太大,数据库维护成本也太高。
为此,MatrixGate 的每个并行任务在执行一段时间后,会向所有取数据的 segment 发送 EOF,即结束符。segment收到结束符后,认为数据已经取完,随之 SELECT * FROM external 子查询执行结束。在INSERT成功后,整个查询完成。
随后,会继续执行 INSERT INTO dest SELECT * FROM external,写入后面的数据。每个接收数据任务的默认执行时间间隔为 100ms,即过 100ms 发送 EOF。也就是说,数据在百毫秒级别时延后就会被查询到,基本接近实时。
从上图可以看出,MatrixGate 的每个数据写入任务内部有很多 Slot,每个 Slot 都会执行 INSERT INTO dest SELECT * FROM external 语句来实现数据从外部表导入到数据表。SQL 语句执行分为如下两个阶段:
- 发送数据
- 重分布与提交
每次发送数据时间窗口为 100ms,不同 Slot 之间发送数据是互斥的(因为一个 Slot 就会占满网络带宽)。发送数据时间窗口到达后,发送 EOF 结束 SELECT 子句。新写入的数据则进入下一个 Slot 的发送数据窗口。
3 结论
从上面可以看出,使用 MatrixGate 这种架构模型包含如下优点:
- 数据从客户端到segment之间没有落盘,实时转发,无本地IO消耗
- 数据解析过程转移到了segment上,充分发挥了分布式系统多机资源
- 多任务并行处理,最大限度利用集群资源
- 微批量,保证了实时性
- 外部表维护操作对用户透明,降低了维护成本
经测试,使用 MatrixGate 进行数据导入与竞品相比,性能可以提升几十倍。具体数据可参考《时序数据库插入性能评测:MatrixDB是InfluxDB的78倍》。