天天看点

IoTDB MPP框架源码解读之SQL的一生(襁褓)新版MPP查询框架的优势原生分布式数据库系列文章的规划

随着Apache IoTDB 0.14.0-preview1 版本成功的在6.30发布,意味着IoTDB的新版分布式框架的主体功能基本完善了,当然还有一些扩展功能因为开发时间比较紧张,并未随这次发版一起进入,如CQ、Trigger、select into等高阶功能。

这次的发版也可以称为IoTDB分布式发展史,甚至也可以称为IoTDB整体发展史上的一个里程碑事件。正如乔老师在我们内部6.30的总结会上说的,这次新分布式框架奠定了IoTDB未来至少5年的基调,这至少5年内,我们都不会再做框架上的大改动了。

新版MPP查询框架的优势

之前IoTDB的查询实现,可以说是为每种查询手写了最佳实现,除了底层共用了与文件交互的SeriesReader外,几乎每种查询都有自己独立的自上而下的流程。这种方式的好处在于前期查询种类较少时,每种查询的性能都能调整到最优;但是随着IoTDB的发展,查询种类越来越丰富之后,我们发现原来的这种方式,扩展性比较差,一个limit和offset的功能竟然会散落在各种查询的内部逻辑里,导致有时候做功能改动时,总会漏改某处。想新增一个查询功能时,也会有很多历史包袱,甚至开发人员需要了解所有种类的查询,去各个种类查询的逻辑中修改。而新的MPP查询框架,遵循传统关系型数据库的规则,定义出基础的查询算子,每种查询由多个查询算子组合而成,相比之下,新MPP的查询框架优势在于:

  1. 添加新功能容易扩展,算子功能更加高内聚低耦合

    a. 对于Limit功能,原来散落在各个不同类型的QueryDataSet中,现在就一个LimitOperator

  2. 统一计算框架、丰富谓词条件

    a. 原来select子句里的计算框架独立于where条件里的Filter,MPP中因为有了Operator和TsBlock的抽象,统一用TransformOperator来进行select子句和where子句的计算

    b. 以前where子句里的谓词只支持一些简单的二元运算符(>, <, ==等),现在得益于统一的计算框架,无需编写冗余的重复代码,天然支持了更复杂的计算表达式,如(where (s1 + s2) * 3 + 4 > s3)

    c. 之前的fill只对group by和单点查询支持,现在因为fill作为独立的Operator,可与其他任意Operator搭配使用

  3. 良好的PlanNode抽象,为后续各种查询优化打好基础。

    a. 如对于limit下推,之前因为每种查询都有自己单独的PhysicalPlan,不利于优化规则编写,只能把push down的代码散落在每一处。而现在可以较为统一的编写PushDownRule

  4. 统一不同Operator间传递的数据结构为内存中列式存储的TsBlock,可存储一列时间戳、任意多列值,便于之后做各种执行优化(如SIMD、Operator内改造成按列操作,提高CPU Cache命中率)。而之前不同查询的不同阶段传递的数据结构不同,最底层SeriesReader向上传递的是按批返回的BatchData(原本是只支持一列时间戳和一列值,引入多元时间序列后,支持了一列时间戳和多列值,但多列值按行存,而非列存,结构不统一);上层计算迭代时,使用RowRecord按行返回。

    a. 目前MPP的实现中因为时间问题,很多代码参照了之前的老代码编写,导致部分Operator也按行返回数据了(TsBlock中只有一行数据),这部分之后需要慢慢改造

  5. 固定查询线程数,引入优先级和时间片的概念,查询执行调度更加可控与可编程。

    a. 旧查询引擎的查询线程数虽然固定,但是没有调度和时间片概念,先进先出,一个查询会一直占用一个查询线程直到拿到FetchSize行数据,这个会导致慢查询长时间持有查询线程,导致后面短频快的查询被阻塞。

  6. 引入背压机制,控制查询总内存

    a. 旧查询引擎在查询执行过程中没有内存控制,查询并发数上升后,很容易爆内存;MPP框架在查询数据传输处引入DataBlockManager和MemoryPool,向MemoryPool申请到内存后,该查询才能继续运行,保证查询内存可控。

原生分布式数据库

这次的MPP框架也意味着IoTDB成为了原生的分布式数据库,0.14.0-preview1中的新版单机IoTDB是由分布式IoTDB的部分组件组合而成的,这是与旧版分布式IoTDB比较大的不同。旧版分布式IoTDB将单机IoTDB作为独立模块,相当于在上层做了一层中间件,去管理下层的很多IoTDB旧单机实例。旧版分布式的这种实现也不利于扩展,原来开发IoTDB旧单机和旧分布式的是两拨人,互相可能也不太了解各自模块的实现,导致一旦旧单机做了一些改动,旧分布式那边可能就无法运行了,需要做相应的适配。所以这次新版分布式,我们也是将IoTDB彻底改造成了原生分布式数据库,如此一来,就不会有之前分布式和单机之间的鸿沟了,因为如今的新单机只不过是分布式的一个特例罢了。

系列文章的规划

早在我研二的时候,其实就有写一个IoTDB查询源码解析的系列,但是因为当时IoTDB的查询如上述所述,实现的太过琐碎,很难整理成一个整体的脉络,从前到后串讲起来。想要写的话,可能每种查询都得单独写一篇文章了。得益于现在的MPP框架,我打算写一个系列文章《IoTDB MPP框架源码解读之SQL的一生》,初步打算以中国古代对各个年龄段的称谓命名,分为襁褓(不满周岁)、孩提(二至三岁)、黄口(十岁左右)、弱冠(二十岁)、而立(三十岁)、不惑(四十岁)、天命(五十岁)、花甲(六十岁)、古稀(七十岁)、耄耋(八十岁)、鲐背(九十岁),共11篇,具体内容如下所示:

  • IoTDB MPP框架源码解读之SQL的一生(襁褓)——即本文,主要介绍这一系列文章的由来和概览;
  • IoTDB MPP框架源码解读之SQL的一生(孩提)——介绍Protocol层,通过Protocol层的封装,IoTDB能够支持多种协议:如IoTDB原生协议、InfluxDB Protocol和MQTT等;
  • IoTDB MPP框架源码解读之SQL的一生(黄口)——介绍逻辑计划生成
  • IoTDB MPP框架源码解读之SQL的一生(弱冠)——介绍分布式计划生成
  • IoTDB MPP框架源码解读之SQL的一生(而立)——介绍聚合查询在分布式计划拆分时的细节
  • IoTDB MPP框架源码解读之SQL的一生(不惑)——介绍不同FragmentInstance间交互的异步数据传输模块
  • IoTDB MPP框架源码解读之SQL的一生(天命)——介绍每个FragmentInstance在DataNode上本地物理执行计划的生成
  • IoTDB MPP框架源码解读之SQL的一生(花甲)——介绍本地执行计划的运行载体Driver的实现细节
  • IoTDB MPP框架源码解读之SQL的一生(古稀)——介绍DataNode上的查询调度模块
  • IoTDB MPP框架源码解读之SQL的一生(耄耋)——介绍查询中的表达式计算的框架
  • IoTDB MPP框架源码解读之SQL的一生(鲐背)——用一个例子,将前面所有的知识串联起来,带领大家亲手在IoTDB中实现一个查询功能