从时序数据的特点说起,揭秘 TDengine 高性能的存储引擎

小 T 导读:了解 TDengine Database 的朋友应该知道,TDengine 在处理时序数据时,有非常卓越的表现。这取决于我们对时序数据特点的挖掘和总结,并且 TDengine 充分利用时序数据的特点,设计了存储模型和查询模型。本文主要介绍 TDengine 的存储引擎设计,看看 TDengine 是如何通过创新数据存储引擎达到超强性能的。

一、时序数据的特点

我们先观察一些时序数据,然后来总结一下时序数据的特点

一些典型的时序数据

上图展示了一些典型的时序数据,横轴是时间,纵轴是采集量,可以看出:

  1. 有些时序数据在很长一段时间内都是固定的值,但是某些时间点也会产生异常跳变,异常检测在时序数据的处理过程中,也是非常重要的一环,但本文不过多展开;
  2. 有些时序数据在一段时间内是一个变化趋势;
  3. 有些时序数据在一定数值范围内会进行波动,有些波动频率高,有些波动频率低;
  4. ……

因此我们总结出了时序数据的特点,如下图所示,我们可以利用这些特点,进行存储引擎的设计。

物联网数据特点

二、TDengine 的数据模型

TDengine Database 的数据模型主要有以下特点:

  1. 一个数据采集点一张表
  2. 一张表的数据在文件中以块的形式连续存放;
  3. 文件中的数据块大小可配;
  4. 采用 Block Range INdex(BRIN)索引块方法。

一个数据采集点一张表的设计逻辑会导致表的数量级膨胀,因此引入 vnode 的概念,对数据进行 Sharding。点击《万字详解TDengine 2.0整体架构设计思路》可以详细了解 vnode 的概念,这里不再过多阐述。

总的来讲,vnode 是时序数据存储的基本单元,一个 vnode 包含一定数量的表(数据采集点),数据的负载均衡、同步是以 vnode 为单位进行的,vnode 可以充分利用多核的特点,提高并发速度。

对于一个 vnode 内的数据,我们按照时间段对数据进行分区(Partition),将同一时间段的数据存储在一个数据文件组中,并以文件组为单位对过期数据进行删除。在我们的设计中,时间段和文件编号是一一对应的,因此,查询某个时间段的数据,我们只需要计算出索引号,就可以到对应的文件中进行查询。

三、 TSDB 存储引擎

TDengine Database 针对时序数据的特点,专门研发了 TSDB 存储和查询引擎。作为一个查询引擎,它提供了基本的查询接口。我们这里主要讲解 TSDB 的存储引擎。TSDB 存储了一个 vnode 中表的 META 信息以及时序数据(采集信息),后者以行和列两种结构存储(TDengine 2.0 开始引入行存储)。时序数据在内存中以 SkipList 方式进行索引的,在硬盘中是以 Block Range INdex(BRIN)方式进行索引的。

TSDB 是什么?

META 数据

TSDB 存储了 vnode 表中的 META 数据。META 包括表/超级表的 SCHEMA、子表 TAG 值、TAG SCHEMA 和子表/超级表的从属关系。META 数据的添加、更新以及删除等操作先在内存中进行,最后序列化并写入硬盘。

META 数据在 TSDB 中是全内存加载的,根据子表的第一个 TAG 值建立一个内存索引,因为只对 TAG 的第一个值索引,所以速度最快。使用 vnode 的 Sharding 方式可以充分利用多个 vnode 资源进行表的过滤查询操作。

META 数据的持久化存储

META 数据写入内存时会同时生成序列化记录,以 append only 形式存储到内存 buffer 中。内存数据达到一定量后触发落盘操作,落盘时,更新的序列化 META 数据以 append only 形式写入硬盘META文件,每张表的最新状态、表的更新和删除都会 append 到 META 文件中,序列化成一条记录。

时序数据

TSDB 也负责存储 vnode 中表的时序数据(采集数据),时序数据在写入时首先会写入到 TSDB 事先分配的内存缓冲区中,当内存缓冲区的数据积累到一定量后,触发落盘,然后进行持久化存储。

TSDB 内存中的时序数据为行存储,因而支持以 append only 形式添加 buffer,从而充分利用已分配的内存资源,缓存足够多的数据进行落盘,有利于一个块的数据量进行积累,有利于压缩。为了便于查询和乱序数据的处理,内存中建立了一个 SkipList 作为内存索引。内存中还维护了已经写入数据的最新时间和最老时间等信息。内存中一条数据的行存储格式如下图所示。

内存中一条数据的行存储格式

时序数据的持久化存储

TSDB 内存中的数据积累到一定量时,会触发落盘。在落盘时,时序数据由行存储形式转化为列存储形式,并维护 BRIN 索引,引入 LAST 文件和 SUB-BLOCK 机制处理文件碎片化。列存储形式如下图所示。

文件存储块

TSDB 工作流程

TSDB 启动时会事先分配一个 BUFFER POOL 作为写入缓冲(默认16 MB*6=96 MB),缓冲区块大小和个数可配,区块个数可修改。META 数据和时序数据从缓冲块申请写入空间,写入引擎向 BUFFER POOL 申请缓冲区块,写满的缓冲区块占总缓冲区块的1/3时触发落盘操作。落盘时,缓冲区块中的数据写入到 META 等文件中,落盘结束后缓冲区块归还给 BUFFER POOL,形成循环机制。查询时,对 MEM,IMEM 以及数据文件中的数据进行合并查询。如下图所示。

TSDB 工作流程

TSDB 设计的优点

  1. 对于单表按照时间段的查询效率很高
  2. 内存行存储充分利用内存,缓存更多数据
  3. 文件中列存储充分发挥压缩算法优势
  4. 避免LSM过多的文件合并
  5. 标签数据与时序数据分离存储

总结

本文主要讲解了 TDengine 的存储引擎,当然,决定 TDengine 高性能和节省 TDengine 存储空间的原因还包括先进的压缩算法以及查询模型的设计等。关注我们,后续会继续详解 TDengine 达到如此高性能的原因。

作者简介:程洪泽,涛思数据联合创始人,TDengine 核心作者。美国密西根大学 EE 硕士,本科毕业于中科大,是中科大郭沫若奖学金获得者。主要研究方向为面向物联网的大数据和机器学习技术。