清华自研时间序列数据库Apache IoTDB原理解析
云智慧 AIOps 社区是由云智慧发起并致力于推动 AIOps 技术发展的开放平台。
该社区专注于运维业务场景,在算法、算力以及数据集的整体服务体系上进行深入探讨,并进行智能运维业务场景的解决方案交流。
该平台旨在促进与各行业客户的紧密合作与技术交流,并共同解决智能运维行业技术难题。通过这种方式推动 AIOps 技术在企业中的有效落地,并助力构建健康、可持续发展的 AIOps 开发者生态。
智能运维领域的数据特点
在运维场景中,指标数据被视为重要的观测项,并且为服务可用性监控以及系统健康度量等场景提供主要的数据来源。架构示意图显示,在这种架构下, 采集器会收集服务器上的各种指标数据, 发送至消息队列, 通过实时流处理和离线计算后最终存储到数据库。

在这个上述场景中,我们往往会遇到以下几种数据挑战:
- 我们日常需要监控的数据指标数量远超百万元,在峰值情况下甚至达到了千万级的数量。
- 日常对指标数据的行为涉及不同时间段内的具体情况。
其中包含几十分钟到数小时内的情况,在一周内的时间段内也常见。
对范围查询的性能有一定要求。
在数据传输过程中受网络设备资源等因素的影响而短时间内出现乱序到达现象丢失关键数据点同时也会产生峰谷潮以及重复数据冗余的问题
归因于服务器或设备固有的局限性,在实际的数据采集过程中难以保证精确的时间同步性问题会导致数据粒度分布出现不协调的现象举例而言对于秒级精度的指标前一次的数据记录时间为2\text{时}3\text{分}4\text{秒}567\text{毫秒}后一次的数据记录时间可能为2\text{时}3\text{分}4\text{秒}568\text{毫秒}\ldots不同指标在同一时间段内各自的数据记录时刻分别为如A指标在某时段的第一笔数据时间为X而B指标在同一时段的第一笔数据时间为Y
至此整体的需求基本已经明确,在做数据库选型时需要满足以下需求:
1)支持数据长时间存储;
2)支持大时间跨度的快速检索;
3)高速的数据吞吐能力;
4)高效的数据压缩比;
该系统能够有效解决数据序列混乱、缺失值问题、数据粒度不一致问题以及重复数据问题等数据质量问题。
智能运维领域的时序数据该如何存储
如何选择适合上述需求的数据库系统?是传统的基于关系型模型的数据库系统、非关系型数据存储技术中的通用方案还是时间序列分析专用解决方案?这些方案是否能够满足上述对数据库选型的需求?
在存储数据时需考虑其固有特性。基于一个实际应用场景的案例分析显示,在某运营商系统中每天会产生约3,00万个监控指标,并且在采集过程中会遇到空值、缺失以及重复等状况,在允许一定延迟的情况下仍需完成每秒超5千万次的 writescale 记录操作,在这种情况下一天下来总共需要处理432亿条记录量的数据量。对于传统的关系型数据库而言,在处理如此庞大的 writescale 和查询响应速度方面均面临诸多挑战。
进一步了解常见的NoSQL数据库体系结构。为了更好地理解这些关键指标数据的特点,在初步学习后会发现这类关键指标数据不仅包含时间戳和数值信息,并且每个记录都带有标签信息以标注来源机器设备。经过分析发现,这类关键指标数据不仅包含时间戳和数值信息,并且每个记录都带有标签信息以标注来源机器设备。具体的数据样本可以通过下图来展示

在通用NoSQL数据库虽然具备较高的处理能力和高效的查询性能;然而为了应对指标的变化需求必须采用一个设备对应一张表或者多个设备共享同一张表的建模方案如图所示。

无论采用单一设备独立建表的方式还是多设备共享一张表的方式,在区分数据来源不同的指标时都仅将tags字段单独设为一列即可。然而不难发现这种混合式建表模式会导致大量tags字段冗余存储的现象存在,并且通用的非关系型数据库在处理数据去重问题时较为棘手主要依赖于两种排重策略:一种是通过外部工具实现的数据去重在此情况下可以在存储阶段完成初步去重;另一种则是允许在查询时借助SQL语句来实现对重复数据的检测和排除。若采用基于字段层面的数据去重策略会导致系统架构复杂度上升;而如果选择依赖SQL进行动态去重则可能无法有效解决粒度卡齐或线性填充的需求。
然而我们之前遇到的一些数据挑战实际上属于时序数据库需要解决的经典问题。市场中存在众多性能卓越的时序数据库产品可供选择,在功能上包括高吞吐量查询响应速度以及高效的去重填充等特性能够满足我们的需求。这些特性能够满足第一章中所提到的数据存储要求。随后我们将深入探讨完全开源的Apache IoTDB的设计理念及其架构特点。
IoTDB的设计
IoTDB的架构
该系统采用基于LSM-Tree架构的设计理念构建了一个列式存储数据库;LSTM树的核心理念在于通过牺牲部分读取性能以换取显著的写入性能提升;通过查看下图所示的IoTDB整体架构图可以看出该系统主要由三个关键组件构成:包括数据处理核心、存储管理核心以及分析推理核心。
数据库引擎的主要职责是处理 SQL 指令,并负责执行包括处理 SQL 指令在内的一系列操作。
存储引擎是核心设计也是TsFile的主要组成部分同时也是IoTDB的关键模块它不仅能够独立支持IoTDB的数据存储功能还能通过特定接口与分析引擎进行数据交互同时该模块还提供了开放的API接口方便外部开发者直接调用获取所需功能
分析引擎主要是用于与开源的数据处理平台对接等。

IoTDB的数据读写流程
该系统采用LSM-Tree的思想进行设计与实现。通过流程图可以看到数据的写入过程如下:经由time detector检测后依据内存中的最大时间戳判断数据是否有序。该系统会将这些无序的数据根据其特性进行分类存储于内存缓冲区memtable中。为了确保断电期间数据不会丢失 该系统会将所有记录操作的结果存储至WAL(Write-Ahead Logging)日志中 完成客户端的所有记录操作。当系统持续进行大量 writes时 该系统会调用 submit flush task任务将 memtable转换为Immutable状态并最终持久化至磁盘作为 Sstable格式的 TsFile 文件 同时当 TsFile文件的数量达到一定阈值时 系统会自动触发合并过程以保证存储效率和稳定性。

在阐述了IoTDB数据写入流程之后,我们接下来探讨IoTDB核心查询流程。如图所示,在客户端发送查询请求时首先通过Antlr4进行sql解析,并在内存中的MemTable、ImmuTable以及硬盘中TsFile中执行查询操作。值得注意的是,在这种情况下 IoTDB会先利用BloomFilter进行初步筛选 然后通过辅助索引进一步确认数据的存在性 因此 在这种情况下 IoTDB会先利用BloomFilter进行初步筛选 然后通过辅助索引进一步确认数据的存在性

TsFile结构
在IoTDB的数据处理过程中, TsFile始终扮演着不可或缺的角色. 通过查看TsFile的结构示意图可以看出, 该系统主要分为两大部分: 一部分是数据存储区域, 另一部分则用于实现高效的索引管理功能.
数据区主要包含三个组成部分:页面表目(Page)、Data Block(即Chunk data block)以及Data Group(即ChunkGroup)。具体来说:
- 每个页面表目(Page)都包含有 PageHeader(即Page Head record)以及一段时间-值编码的关键-值对。
- 每个 Data Block(即Chunk data block)则由多个 Pages(即Pages)与一个 Chunk Header(即Chunk Head record)构成。
- Data Group(即ChunkGroup)则用于存储某单一实体(Entity) 所持续的一段时间内的记录信息。每个 Data Group(即Data Group)都是由若干个 Chunks(即Chunks)、1个 byte-level separator with value 0x00以及1个 Chunk Footer构成。
主要包含三个关键组件:时序索引(TimeseriesIndex)、时间序列索引(IndexOfTimeseriesIndex)以及布隆过滤器(BloomFilter)。每个时序索引都包含一个头信息块和多个数据块索引。其中每个时序索引都包含一个头信息块和多个数据块索引。每个头信息字段记录了该时间序列的基本属性和统计参数。这些数据块的存储位置(offset)以及相关的统计参数被详细记录下来。为了方便定位各个时序数据在存储中的位置,我们引入了专门的时间序列索引模块IndexesOfTimeSeriesIndexes这一模块可以帮助快速定位各个时序数据在存储中的位置。如图所示,在TsFile中存在两个实体节点d1和d2。这两个实体分别包含了三个物理量s1、s2、s3作为属性,并且它们各自对应着不同的时间序列结构。具体来说,在TsFile中存在两个实体节点d1和d2这两个实体分别包含了三个物理量s1、s2、s3作为属性,并且它们各自对应着不同的时间序列结构

TsFile索引构建
TsFile中的所有时间序列节点构成了一个多叉层次结构的数据存储模型。该模型主要包含实体存储区和物理量存储区两大系统模块。举例说明该索引体系结构:假定系统的层次深度设定为6层,则在当前配置下可容纳总计22,500条时间序列数据;在这种组织架构下,在进行任意一条时间序列的数据检索时(如图所示),系统将依次完成6次磁盘读取操作以完成数据定位任务

上面这种方式看似磁盘I/O操作量相对较高,主要源于我们所设计的数据结构中树节点的分支因子较小。因此,在这种情况下整体决策树的高度会相应较大。如果我们适当提升节点的最大分支因子至300,在实体索引部分仅需高度为2的一棵子树即可容纳9万条设备存储信息;同时在物理量索引部分同样通过两层结构也能支持9万条物理量数据存储需求。这样一来整个时间序列索引体系最多可容纳81亿条数据记录,并且此时通过磁盘I/O操作快速定位所需数据仅需4次操作就可以完成定位过程
TsFile查询流程
在熟悉了TsFile的架构和索引机制后,在了解了其工作原理的基础上,请问您知道IoTDB是怎样在TsFile内部执行一次查询的具体流程吗?以select s1 from root.ln.d1 where time>100 and time<200为例,请问您能否详细说明其操作步骤及流程图?首先根据关键字对数据进行分组处理。接着对分组后的数据按时间戳进行排序处理。最后采用二分查找法快速定位目标行。
1)读取TsFile MetadataSize信息
2)根据TsFile MetadataSize和offset获取TsFile MetaData的位置
获取Metadata IndexNode中的信息,并利用MetadataIndexEntry中的名称确定设备root.ln.d1的位置。
从设备root.ln.d1中获取了offset偏移值,并通过这些偏移信息进一步推导出TimeSeries Metadata中的相关信息以最终定位到s1
5)基于ChunkMetadata记录的数据集中的startTime和endTime字段,在对比分析我们查询的区间(100, 200)与root.ln.d1设备下测点s1之间的关系时,能够得到该设备下测点s1的偏移量数据。
6)根据s1中的偏移量可以直接获取到ChunkGroup
按顺序利用ChunkGroupHeader和ChunkHeader来定位对应的Chunk数据后,在逐个读取Page内的PageHeader时,请注意当时间区间位于(100, 200)之间时,则直接读取该页面中的PageData内容

总结
本文对IoTDB的读写功能及TsFile核心模块的架构设计进行了简要介绍。然而IoTDB的整体设计方案较为复杂,包含诸多复杂细节,在此不做深入探讨。
写在最后
近年来,在数字化转型加速推进的时代背景下
社区在不同时间段依次推出了数据可视化编排平台FlyFish、运维管理平台OMP、云服务管理平台摩尔平台及其他相关产品
可视化编排平台-FlyFish:
项目介绍:https://www.cloudwise.ai/flyFish.html
Github地址: https://github.com/CloudWise-OpenSource/FlyFish
Gitee地址: https://gitee.com/CloudWise/fly-fish
行业案例:https://www.bilibili.com/video/BV1z44y1n77Y/
部分大屏案例:

