乐趣区

关于tdengine:TDengine30计算查询引擎的优化与升级

在 8 月 13 日的 TDengine 开发者大会上,TDengine 计算引擎架构师廖浩均带来题为《TDengine 3.0——全新计算查问引擎的设计》的主题演讲,具体论述了 TDengine 3.0 计算查问引擎技术的优化与降级。本文依据此演讲整顿而成。

点击【这里】查看残缺演讲视频

3.0 中的查问引擎在 2.0 版本的根底上进行了重写,在承继 2.0 查问引擎既有劣势和技术个性的根底上,在工程实现和整体架构设计上有了极大的改善和晋升。

总体来讲,TDengine 3.0 大幅加强了对 SQL 语义的反对、欠缺了 SQL 查问语法;强化了整体执行框架及 SQL 查问的调度能力;提供更好的执行工作隔离机制,对于谬误具备更好的容忍度;提供反对存算拆散架构的能力。它次要蕴含以下几个方面的特点:

1. 反对规范 SQL 查问语法

  • 全新的 SQL 解析器、打算生成及优化器,引入伪列 (_wstart/_wend/_qstart/_qend/_rowts) 等语法概念和新的(分组)关键词,全面加强查问语法。
  • 打消 2.x 版本中对时序数据查问施加的诸多限度,能够像应用关系对象数据库查问引擎一样应用 SQL 查问语法。
  • 反对标签 / 一般列的运算,标量函数和矢量函数的嵌套、任意字段(列)的分组 / 排序 / 聚合、无限度的多级子查问。

2. 反对存算拆散架构

  • 反对存算拆散的体系架构,反对集群内计算节点动静、疾速、弹性部署。
  • 查问引擎可能将查问按需调度到计算节点,无效升高存储节点(Vnode)计算资源的压力。
  • 原生的流批一体化查询处理
  • TDengine 3.0 原生反对流计算和交互式查问。流计算引擎也应用 SQL 语言作为交互路径,因而,流计算引擎间接应用查问框架的 SQL 解析解决、执行打算生成、查问执行算子、用户利用交互等诸多方面的能力。

3. 更优良的鲁棒性和韧性设计

  • 为防止用户谬误导致的主服务宕机,用户定义函数的执行服务(UDFD)与 TDengine 的主过程进行隔离,UDFD 能够在解体当前主动被从新拉起。
  • 对于 SQL 查问转化为调度器的查问工作(Job),Job 在被合成当前会按层级调度执行,并且同档次工作互相独立执行,单个工作失败当前能够从新拉起进行解决。
  • 对于不同分层的工作(Task),失败工作能够从上游工作的后果缓存(Sink Node)中再次申请数据并尝试从新执行。

4. 更好的执行可观测性

  • 将查问打算、执行开销、查问中 Task 的动静执行状态等相干信息通过命令行交互界面(CLI)出现给用户,助力用户定位查问执行的瓶颈,并进行针对性的优化解决。
  • 提供 Explain 指令,用户能够通过 Explain 取得查问执行的打算、各算子执行开销等信息,为用户层面 SQL 改写和优化性能提供反对。
  • 通过心跳信息,将服务端执行状态推送到调度器,并反馈给用户,让用户更好确定查问执行的状态。

5. 反对数据集快照查问

  • 全面内置版本化的查问反对能力,能够为事件驱动流计算引擎提供指定版本和工夫范畴的历史数据追溯查问。
  • 为双(多)活集群提供数据同步版本化读取能力。

查问引擎架构设计

上图是 TDengine 3.0 的查问引擎架构,右边的 Query Wrapper 运行在 Vnode 和 Qnode 上,它外面蕴含了一个执行器,其中的 Index 模块是解决 Vnode 中存储的标签数据的索引。执行器自身并不解决与调度器的交互行为,Query Wrapper 负责解决执行 Task 动作并将后果缓存在 Sink Node;此外,执行器会调用 function 模块和 ScalarFunc 模块进行计算解决,并在必要的状况下通过 function 模块调用 UDF 服务。

两头的模块是由 Query Wrapper 和 libtaos.so 所共享的,外面蕴含的是 function(零碎中其余所有函数的定义模块)和 ScalarFunc(标量函数以及过滤的模块)。须要留神的是,在 function 模块中还蕴含了一个 UDFD 的计算模块,它是用户定义程序的执行服务端,运行在一个独立的过程空间。

最左边的模块是运行在用户过程空间中的驱动 libtaos.so,Driver 是 libtaos.so 里的一个驱动,它负责串联各个模块来实现查问的操作,Parser 用来调用分词器与语法分析器进行语法分析,生成语法树(AST),Catalog 负责从 Mnode 和 Vnode 中获取各种元数据,Planner 负责将 AST 和 元数据信息转化为逻辑执行打算、物理执行打算、并进行执行打算重写,最初生成分层查问打算,Command 负责执行本地化查问操作。

相比于 2.0,3.0 在模块化和工程化上做了较多的优化,次要能够归结为以下几方面:

  • 客户端不参加计算,计算过程在服务端实现
  • UDF 在独立的过程空间中运行,与主服务过程隔离
  • Qnode-aware 的自适应查问任务调度策略
  • Index-aware 的标签过滤机制
  • 节点间 / 算子间采纳规范的列格局(columnar data)数据进行数据传递
  • 进行计算的节点会通过心跳将查问执行信息返回给调度器
  • 重写查问框架,造成了高内聚、低耦合的框架模块

时序数据查询处理流程

首先,Parser 会把图上的 SQL 语句转成一个形象语法树,之后会依靠于 Catalog 节点拉取相应的元数据,而后将信息传递到 planner,生成如图上所示的逻辑打算。之后在逻辑打算层面会生成四个 node,自底向上别离是 Scan、Partition、Window、Project,它们别离对应 SQL 语句里不同的几个关键词。

逻辑打算传递上来,最初会在 planner 中生成一个物理打算。上图所示的红色虚线以下局部(Partial Agg)是对物理打算的执行,包含 VgID:#1 和 VgTD:#2 两个子工作;虚线下层是一个 Global Merge,是最初的一个聚合阶段,Partial 聚合后果通过 merge sort 后再次进行全局聚合,造成后果当前送到计算节点本地的 sink node 进行缓存,期待 scheduler 来拉取最初的计算结果。

在之前逻辑打算中的 Logic partition 在物理打算中却看不到了,其实 Partition 分组逻辑是被下沉到了 TableScan 实现,这是一个优化操作。依照标签分组机制,同一个表中数据肯定同一分组,咱们在 TableScan Node 中建设表分组的映射关系即可,再将数据块打上 GroupID 就实现了分组操作,不须要对每一条数据进行扫描。

物理打算的具体实现在 TDengine 零碎执行的日志外面能够看到,感兴趣的同学能够去看看,零碎中的物理打算采纳 JSON 模式的文本出现。

在实际操作中,物理打算最终会传递到调度器中执行,调度器工作的元素就是 Task 和 Job,每个 SQL 查问是一个 Job,每个 Job 由若干个 Task 形成,每个 Task 蕴含了其执行的节点信息、工作 ID、须要执行的物理打算等信息,不同的 Task 会造成一个层级化的树形构造。

物理打算传递到调度器后,虚线上面的两个 Partial Agg 会被转化成 Subplan #1(level 1) 和 Subplan #2(level 1),这两个执行打算会别离发送到不同的 Vnode 进行执行。虚线下层也是一个 Task [Subplan #0(level 0)],等上层两个 Task 执行实现或至多返回第一条记录时,调度器就会拉起上一层 level 0 的 Task 进行执行,它是层级化的执行形式。

时序数据查询处理的整个流程

时序数据查询处理的整体流程如上图所示,其中计算节点的抉择次要有以下几点策略:

  • 局部聚合计算的计算节点因为聚合计算下推,要求在保留有数据的 Vnode 上进行,以减小数据在集群中传输带来的开销;
  • 对于全局聚合,能够在集群中所有可用节点中任意选取,为了减小数据在集群中传输的开销,个别会抉择进行部分聚合(Partial Agg) 的节点进行全局聚合;
  • 如果在集群中存在 Qnode(计算节点),调度器会优先将后续阶段的计算调度到计算节点上进行计算。

时序数据查问优化策略

排序打消

排序是查问过程中,I/O 和 CPU 开销都十分高的操作。如果查问后果要求依照 timestamp 排序(升序或降序)输入,因为时序数据在 TDengine 中就是依照工夫序列存储,所以排序的操作可用间接打消,只须要将 TableScan 指定为升序 / 降序扫描返回后果即可。

排序优化

操作逻辑如上图所示。对于分布式的后果排序输入,排序操作充分利用时序数据有序性,如果咱们应用归并排序代替齐全排序,就能防止在规范外存排序过程中触发的 IO 操作。这一操作次要在超级表查问的场景中应用较多。

数据代替

从这一条优化策略开始,须要给大家补充一下对于 SMA 的背景常识。SMA 是 Small Materialized Aggregates 的简称。对于每个落盘的数据块(Block)都会生成一个对应的 SMA 信息,其中蕴含了每列数据的最大值、最小值、NULL 数量信息。这些信息针对数值型列都存在,必要时能够代替数据参加计算。绝对于具体的数据来说,SMA 占据的磁盘空间十分小,因而可能极大地晋升某些品种的查问性能。

基于 SMA 的优化策略之一就是数据代替。针对每个函数,TDengine 定义了其须要的数据范畴,上表是一些聚合函数的数据计算需要列表。在 SQL 解析阶段,如果确认其最终的数据需要是 Small Materialized Aggregates (SMA),最终只会读取 SMA 进行计算。但并非任何状况下都能够应用 SMA,对于所有标量函数,一旦在查问申请外面呈现,SMA 是不参加到计算外面来的。

在应用 SMA 时,查问优化器会通知执行节点,只须要去读取每个 block 的 SMA 信息就行,在整个查问过程中,执行器不会再去读取任何一条具体的数据,所以它的流程十分快。

数据扫描优化

TDengine 中定义了数据程序敏感型函数,在 SQL 语句中应用该类型的函数会触发优化器应用特定的数据文件扫描策略。在 SQL 语句 SELECT last(ts),count(*) FROM foo_table_name 中,Last 函数返回最初一个非 NULL 值,所以逆序扫描可能更快取得后果。而 count 函数对于扫描的程序并不敏感,因而,优化器会指令 TableScanNode 采纳逆序扫描策略。

动静裁剪 Block

对于某些查问函数,例如:last/first/top/bottom 查问,能够应用两头后果来动静裁剪读取的 block 的信息。

在执行过程之中,依据两头计算的后果及以后后果能够确定,下一个须要扫描的 block 是否须要真正地读取。仍然以 last 为例 SELECT last(ts) FROM foo_table_name,在每次须要读取下一个 Database 时,首先应用 ts=100 过滤每个数据块,如果数据块蕴含的工夫范畴晚于 ts=100,才会读取该数据块数据并进行计算,大于 ts=100 的就主动跳过,这就实现了动静裁剪 Block 的性能。

基于 SMA 的预过滤

在进行 Last 查问时,SMA 会被用来做预过滤,其中保留了与之对应的数据块的四项统计信息:最大值、最小值、和 NULL 的数量。所有的针对数据的过滤条件首先利用在 SMA 之上,SMA 满足过滤条件当前,再读取数据块,而后再次进行针对数据的过滤。

以 SELECT last(ts),count(*) FROM foo_table_name WHERE k > 20 为例,这外面有一个过滤条件是 K 大于 20,如果其中存储着对于 K 那一列的最大值、最小值信息,就能够以此为条件进行过滤,如果其中的 MAX 值都小于 20,那么这个 block 也会在 TableScanNode 外面读取实在数据之前就抛弃。

Interval SMA

时序数据库(Time Series Database,TSDB)一个重要的利用场景就是为看板(Dash Board)提供定时的查问(Standing Query, 利用或程序依照固定频率收回的查问)反对,看板利用以固定的频率向利用及时序数据库收回查问执行,并在可承受的工夫范畴内要求取得查问后果。

针对 Standing Query 的执行需要,为了晋升响应速度,节约反复计算带来的资源开销。在 TDengine 3.0 中提供了应用流计算引擎的异步 interval SMA(将针对工夫窗口的聚合查问转化为投影查问,因为计算结果曾经通过流计算引擎计算实现并写回到 TDengine)。

事实上,上述优化并不是 3.0 查问计算引擎里蕴含的所有优化策略,只是波及到了几个较为经典的查问场景。对于查问引擎的优化是十分琐碎的,有些优化策略只针对一种场景,甚至如果改写一下语句程序可能就不失效了。如果大家还想理解更多的查问引擎优化策略,能够去 GitHub 上查阅 3.0 的代码,或者间接下载进行体验。

结语

接下来,对于 TDengine 查问引擎的优化,大抵分为以下几点:

  • 标量计算库采纳 SIMD 进行优化:采纳 SIMD 指令减速标量计算的执行速度
  • 反对多种脚本语言的 UDF/UDAF:3.0 当初反对 C 语言定义的 UDF/UDAF,后续将反对脚本语言定义的 UDF/UDAF,提供更便捷的应用体验
  • 反对高精度数值类型和二进制类型数据:高精度 Decimal 类型和 binary(1MB)类型数据的存储、读取和查问
  • 欠缺查问优化器:查问优化器针对更多的查问场景和用例提供高效率的优化,升高 SQL 执行的复杂程度
  • 反对多源数据的联邦查问:反对应用内部数据源的联邦查问,通过定义连接器,将其余的数据平台作为查问的根底数据源
  • 提供更丰盛的时序相干计算及剖析函数和 SQL 查问语法:局部聚合函数将反对 CASE/WHEN、CTE 语法、SQL Hint,以及针对数据分析利用的需要,并提供更多的内置 SQL 函数

除此之外,后续咱们还将欠缺查问内存管制和工作执行 / 调度策略,在现有的随机分配任务的根底上,基于 Qnode 的运行负载情况,调度查问工作的执行;提供更好的内存控制策略,升高百万 / 千万级别表查问过程中元数据追踪带来的内存抖动;针对查问范畴进行横向的数据查问范畴切分,在同一个 vnode 中并行拉起多个查问执行同一个查询处理。

总而言之,前面咱们还有更多的工作要进行,会持续优化 TDengine 3.0 查问引擎的性能,也欢送更多的 TDengine 关注者和支持者交换利用体验,帮忙咱们提高。


想理解更多 TDengine Database 的具体细节,欢送大家在 GitHub 上查看相干源代码。

退出移动版