资讯专栏INFORMATION COLUMN

Skywalking IoTDB存储插件设计说明

paulquei / 2272人阅读

摘要:目前已提交至社区,正在接受社区评审。表示统计数据,是通过脚本或硬编码对源数据进行聚合分析后生成的存储模型。由于该方案丢失了需要索引的,所以需要通过硬编码记录需要索引的及。

该项目来自于开源软件供应链点亮计划 - 暑期2021的Apache IoTDB - Apache Skywalking适配器项目。目前已提交至Skywalking社区(IoTDB storage plugin #7766),正在接受社区评审。

实现思路

项目目标是为Skywalking开发一个使用IoTDB进行相关数据读写的适配器。可以参考Skywalking目前已有的采用InfluxDB、H2和Elasticsearch的适配器。Skywalking给出了存储拓展的开发指南,参考链接。此外,需要使用Skywalking和InfluxDB的调试过程,以便充分了解Skywalking的接口和使用方式。

IoTDB建议使用其提供的原生客户端封装: Session或Session Pool与IoTDB服务端进行交互。

开发平台:Win10,IoTDB版本:0.12.2

相关概念

Skywalking的存储模型

Skywalking 8.0+的存储模型大致分为4类:Record,Metrics,NoneStream,ManagementData。它们都实现了StorageData接口。StorageData接口必须实现id()方法,下面分别介绍4类存储模型:

  • Record:Record大部分是原始日志数据或任务记录。这些数据需要持久化,无需进一步分析。所有Record类的模型均具备time_bucket字段,用于记录当前Record所在的时间窗口。具体例子有:SegmentRecord,AlarmRecord,BrowserErrorLogs,LogRecord,ProfileTaskLogRecord, ProfileThreadSnapshotRecord,TopN。

    • SegmentRecord:Trace Segment明细记录模型。由skywalking-trace-receiver-plugin插件接收并解析Skywalking Agent发送来的链路数据后得到TraceSegment。
    • AlarmRecord:报警明细记录模型。在指标触发报警规则时,会产生对应的报警明细数据模型。
    • TopN:TopN是采样模型,具备statement字段(用于描述采样数据的关键信息),latency字段(用于记录采样数据的延迟),
      trace_id字段(用于描述采样数据的关联分布式链路ID),service_id字段(用于记录服务ID)。目前采样模型默认只有TopNDatabaseStatement。
      • TopNDatabaseStatement:按照延迟排序的DB采样记录。
  • Metrics:Metrics表示统计数据,是通过OAL脚本或硬编码对源(Source)数据进行聚合分析后生成的存储模型。它的生命周期由TTL(生存时间)控制。所有Metrics类的模型均具备time_bucket和entity_id字段。例如:NetworkAddressAlias,Event,InstanceTraffic,EndpointTraffic, ServiceTraffic,EndpointRelationServerSideMetrics,ServiceInstanceRelationServerSideMetrics, ServiceInstanceRelationClientSideMetrics,ServiceRelationServerSideMetrics,ServiceRelationClientSideMetrics

  • NoneStream:NoneStream基于Record,支持time_bucket转换为TTL。例如:ProfileTaskRecord

  • ManagementData:UI模板管理相关的数据,默认只有一个UITemplate实现类

部分参考《Apache Skywalking实战》第9章:SkyWalking OAP Server存储模型

IoTDB的数据模型

参考IoTDB官方数据模型介绍。简单来说,可以用树结构来认识IoTDB的数据模型。如果按照层级划分,从高到低依次为:Storage Group -> (LayerName) -> Device -> Timeseries。从最上层到其下某一层称为一条路径(Path),最上层是Storage Group,倒数第二层是Device,倒数第一层是Timeseries,中间可以有很多层,每一层姑且称之为LayerName。

值得注意的是,每个Storage Group需要一个线程,所以Storage Group过多会导致存储性能下降。此外,LayerName的值存储在内存中,而Timeseries的值及其下的数据存储在硬盘中。

Skywalking的IoTDB-adapter存储方案

概念划分

Skywalking的每个存储模型可以认为是一个Model,Model中包含了多个Column,每个Column中具备ColumnName和ColumnType属性,分别表示Column的名字和类型,每个ColumnName下存储多个数据类型为ColumnType的数据Value。从关系型数据库的角度来看的话,Model即是关系表,Column即是关系表中的字段。

方案一:类似关系型数据库的存储方案(无法实现)

将Skywalking的所有存储模型都写入IoTDB的一个存储组中,例如root.skywalking存储组。Model对应Device,Column对应Timeseries。即Skywalking的“Database -> Model -> Column”对应到IoTDB的“Storage Group -> Device -> Timeseries”。该方案的IoTDB存储路径只有4层:root.skywalking.ModelName.ColumnName。该方案的优点是逻辑清晰,实现难度较低,但由于数据都存储在硬盘上,查询效率相对较差。

验证结果:该方案无法实现
原因:部分存储接口需要实现group by entity_id的查询功能,但IoTDB只支持group by time。需要采用方案二并通过group by level来实现。

方案二:引入索引的存储方案

由于IoTDB的每个LayerName存储于内存中,可以将其认为是一种索引,可以充分利用LayerName的这个特性提高IoTDB的查询性能。

依然将Skywalking的所有存储模型都写入IoTDB的一个存储组中,例如root.skywalking存储组。Model对应一个LayerName,需要索引的Column也对应于LayerName,不过LayerName并不存储ColumnName,而是存储对应的Value,相当于需要索引的一个Column的不同Value存储在同一分支下的同一层。不需要索引的Column依然对应Timeseries,即路径的最后一层。最终将导致同一个Model的数据分散到多个Device中。

由于该方案丢失了需要索引的ColumnName,所以需要通过硬编码记录需要索引的ColumnName及ColumnType。此外为了避免存储的混乱,还需要保证一个Model下多个索引Column的顺序。计划通过硬编码存储Model需要索引的Column的存储顺序。

该方案的IoTDB存储路径长度是不定的,索引的Column越多,路径的长度越长。例如:

当前有model1(column11, column12),model2(column21, column22, column23),model3(column31),下划线说明该字段需要索引。

  • 需要具备索引的Model:
    • root.skywalking.model1_name.column11_value.column12_name
    • root.skywalking.model2_name.column21_value.column22_value.column23_name
  • 不需要具备索引的Model:
    • root.skywalking.model3_name.column31_Name

插入:

-- 插入model1insert into root.skywalking.model1_name.column11_valuevalues (timestamp, column12_value);-- 插入model2insert into root.skywalking.model2_name.column21_value.column22_valuevalues (timestamp, column23_name);

查询:

-- 查找model1的所有数据select *from root.skywalking.model1_name;-- 查找model2中column22_value="test"的数据select *from root.skywalking.model2_name.*.test;-- 按照column21分组统计model2中column23的和select sum(column23)from root.skywalking.model2_name.*.*group by level = 3;

该方案的优点是实现了索引功能,类似InfluxDB的tag,但逻辑较复杂,实现难度较大。另一方面还需进一步确定哪些Column需要作为索引列,这一点可以参考Elasticsearch(StorageEsInstaller),InfluxDB(TableMetaInfo),MySQL(MySQLTableInstaller)的实现,以及ModelColumn的isStorageOnly属性。

该方案将部分数据通过LayerName存储在内存中,在海量数据的情况下可能会导致内存开销较大。此外,由于同一个Model的数据被分散到了多个Device中,所以查询往往需要跨多个Device进行查询。但在这一方面,IoTDB对于跨Device的聚合查询、排序查询、分页查询的支持还不够完善,在一些情况下需要使用暴力法将所有符合条件的数据都查出来,然后再自行实现聚合、排序、分页的功能。具体描述可参考下文在IoTDB社区提交的Issue和Discussion。

已确定采用索引的字段

id
entity_id
node_type
service_group
service_id
trace_id

此外,可以将time_bucket转换为IoTDB自带的时间戳timestamp后进行存储,而无需另行存储time_bucket

方案的性能测试

参考来自开源软件供应链点亮计划 - 暑期2021的兼容InfluxDB协议或客户端项目的设计文档,可以看到,使用索引的情况和不使用索引的情况在查询时间上有数倍的差距。

Skywalking-IoTDB适配器的设置参数

  1. host,IoTDB主机IP,默认127.0.0.1
  2. rpcPort,IoTDB监听端口,默认6667
  3. username,用户名,默认root
  4. password,密码,默认root
  5. storageGroup,存储组名称,默认root.skywalking
  6. sessionPoolSize,SessionPool大小,默认16
  7. fetchTaskLogMaxSize,在一次请求中获取的TaskLog数量的最大值,默认1000

遇到的问题及解决方案

问题1:Skywalking部分存储接口要求order by查询,但IoTDB仅支持order by time。这本来可以使用选择函数top_kbottom_k来过滤数据。但在使用索引方案的情况下,由于数据点分散在多个device中,top_kbottom_k函数无法过滤数据,也无法使用类似group by level的合并device的查询方法,具体的描述可参考:Discussion #3888, Issue #3905

  • 解决方案:目前除暴力法全部查询出来再排序以外暂无其他解决方案。

问题2:Skywalking部分存储接口要求group by entity_id的分组聚合查询,但IoTDB仅支持group by timegroup by level

  • 解决方案:采用方案二,并将entity_id作为LayerName存储,通过group by level实现分组查询。

问题3:在使用索引方案的情况下,由于数据点分散在多个device中,聚合函数的使用必须要通过group by level才能正确统计数据。但在此情况下,group by levelwhere同时使用得不到预期的结果。应该要加上align by device语义才行,但加上以后就会引起IllegalPathException,推测是因为IoTDB不能同时支持group by levelalign by device的语义。具体描述可以参考Discussion #3907

  • 解决方案:运用align by devicewhere过滤查询所有数据后自行实现聚合函数的功能。

问题4:Skywalking的存储接口要求模糊查询,但IoTDB使用like的模糊查询并不支持跨device查询,不适用于方案二。此外,IoTDB的字符串函数string_contains只能返回true/false,并不会对数据进行过滤。具体描述可以参考:Issue #3945

  • 解决方案:最初使用select *, string_contains获得查询结果后再根据true/false循环过滤。后来@ijihang提交的PR#3953 ,使like支持跨device查询和align by device。所以该问题可以直接使用类似MySQL的模糊查询即可,再次感谢。

问题5:Skywalking的存储接口要求模糊查询和过滤查询。由于采用了索引方案,所以需要跨device查询,但使用string_contains函数的过滤查询对多个device之间采用了and的语义,无法正确过滤数据,如果是or的语义应该就可以正确过滤数据了。同时string_contains也不支持使用align by device。以上这种情况仅支持对time的过滤。

  • 解决方案:最初使用select *, string_contains from root.skywalking.xxx.* where time > ?获得结果后自行对其他条件过滤。后来采用类似问题4的解决方案,直接使用类似MySQL的模糊查询和过滤查询即可。

总结

当前的存储方案还存在使用暴力法进行查询的情况,即使通过Skywalking社区的审查,性能也是不足的,具有数据传输开销大、系统处理资源占用大的问题。暂时还没有好的方法可以解决,要等后续IoTDB针对这类场景增加不同语义的关键字或者完善现有的查询语义才行。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121619.html

相关文章

  • 手把手教你搭APM之Skywalking搭建指南(支持Java/C#/Node.js)

    摘要:通过跟踪请求的处理过程,来对应用系统在前后端处理服务端调用的性能消耗进行跟踪,关于的介绍可以看这个链接,大规模分布式系统的跟踪系统作者刀把五链接来源知乎著作权归作者所有。 手把手教你搭APM之Skywalking 前言 什么是APM?全称:Application Performance Management 可以参考这里: 现代APM体系,基本都是参考Google的Dapper(大规模...

    ingood 评论0 收藏0
  • windows系统下skywalking的安装和配置

    摘要:安装可以去下载最新版本的压缩包,然后解压。然后进入目录下,直接双击即可运行然后访问即可看到的登录页面初始账号和密码均为登录进去即可看到下图因为还没有配置登录进来之后是没有数据的。 skywalking安装 可以去http://skywalking.apache.org/downloads/下载最新版本的skywalking压缩包,然后解压。 然后进入/apache-skywalking...

    AaronYuan 评论0 收藏0
  • 服务迁移之路 | Spring Cloud向Service Mesh转变

    摘要:服务网关服务网关涵盖的功能包括路由,鉴权,限流,熔断,降级等对入站请求的统一拦截处理。具体可以进一步划分为外部网关面向互联网和内部网关面向服务内部管理。应用服务应用服务是企业业务核心。到此实际上已经完成服务迁移工作。 导读 Spring Cloud基于Spring Boot开发,提供一套完整的微服务解决方案,具体包括服务注册与发现,配置中心,全链路监控,API...

    rickchen 评论0 收藏0

发表评论

0条评论

paulquei

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<