作者:段雪林
小T导读:SENSORO(北京升哲科技有限公司)是一家当先的物联网与人工智能独角兽企业。作为城市级数据服务提供商,公司在新一代信息技术畛域领有外围研发能力,在国内首次实现物联网与人工智能畛域端到端、一体化的技术与产品能力,蕴含自研物联网通信芯片、通信基站、智能感知终端和智能视觉终端及外围数据平台等。SENSORO 面向城市基础设施与外围因素提供全域数字化服务计划,通过将多项外围自研关键性技术深刻利用到智慧城市、农村振兴、区域治理、社会民生等畛域,打造物联网与人工智能利用的数字利用标杆,赋能我国城乡数字经济的高质量倒退。
建设城市级传感器网络所波及的传感器品种非常多样,由此产生的数据量也非常宏大, 如果只是应用MySQL、PostgreSQL等OLTP零碎进行数据的简略存储,不仅会产生很多问题,而且其程度扩大能力也无限,同时也因为没有专门针对物联网数据进行优化而不足足够的压缩成果,数据存储老本很高。
在零碎开发初期,联合之前的教训咱们先是抉择了Apache Druid作为存储传感数据的数据库,然而在应用过程中却遇到了各种各样的问题,这使得咱们将眼光转移到了TDengine这款时序数据库。事实上,在TDengine开源之初咱们就留神到了这个新兴的时序数据库,浏览过后公布的白皮书与性能测试报告时惊艳感由衷而生,随即联系到了涛思的同学们,进行了更深刻的交换与测试。
但因为平台波及的非凡数据模型,单干便始终搁置了下来。次要问题在于数据有A、B两个维度且是多对多关系还会随工夫变动,基于A创立子表(此时无奈将B设置成tag列)就无奈通过B进行聚合查问,还须要破费较大的工夫与精力革新成TDengine特有的超级表构造。之后TDengine也通过了多个版本迭代,反对了join查问,而咱们的数据模型也产生了变动,迁徙到TDengine时不再须要做出很多的零碎模块改变。
一、基于Apache Druid现存零碎的问题
基于Apache Druid,零碎最大的问题就是保护老本了。Druid划分了Coordinator、Overlord、Broker、Router、Historical、MiddleManager六个过程,要实现残缺的集群性能,其还须要Deep Storage (反对S3和HDFS),Metadata Storage(典型如MySQL、PGSQL),以及为实现服务发现与选主性能而须要的ZooKeeper,由此也能够看出Druid是一套极为简单的零碎。
同时,Druid对外部的各种依赖也导致运维同学在解决一些问题时,会间接或间接地影响到它的运行,比方咱们将S3的AccessKey进行规范化解决——由以前的全局通用改成某个bucket惟一,或者将PGPool降级,都会影响到Druid。而且Druid针对每一个过程和内部依赖都有厚厚的几页配置项,且从JVM本身来看,不同过程、配置、MaxDirectMemorySize都会重大影响写入查问性能。如果你要从官网文档的配置页面从顶划到底,可能会把手指划抽筋。
基于Apache Druid 的零碎架构(Druid每个过程都独自的部署并有不同的配置)
为了节俭存储老本,咱们在部署Druid集群时对于Historical节点采纳了多种不同的机器配置,在近期数据的解决上,机器装备SSD硬盘并设置较多正本数。这导致数量最多的Data Server节点,有一些不能与Middle Manager共享,同时不同的节点因为装备了不同核数CPU与内存,对应的JVM配置和其余线程池配置也不同,进一步加大了运维老本。
另外,因为Druid的数据模型分为Primary timestamp、Dimensions、Metrics,而Metrics列只能在启用Druid的Rollup时才会存在,而Rollup意味着写入时聚合且数据会有肯定水平的失落。这种状况下,想把每行数据都原原本本地记录下来,只能把数据全都记录在Dimensions列,不应用Metrics,而这也会影响数据压缩以及某些场景的聚合查问性能。
此外还有一些问题如Druid的SQL编译性能问题、原生查问简单的嵌套构造等在此便不再一一列举,总之基于上述问题咱们决定再次具体测试一下TDengine。
二、与Druid的比照
导入雷同的两份数据到Druid和TDengine中,以下为在三节点(8c16g)环境下,100万个传感设施、每个传感设施是40列(6个字符串数据列、30个double数据列以及4个字符串tag列),总计5.5亿条记录的后果。这里要留神一点,因为数据很多为随机生成,数据压缩率个别会比真实情况要差。
- 资源比照:
- 响应工夫比照:
1. 随机单设施原始数据查问
1) 查问后果集100条
2) 反复1000次查问,每次查问设施随机指定
3) 查问工夫区间别离为:1天、7天、1月,
4) 统计查问耗时的最大值、最小值、平均值
5) SELECT * FROM device_${random} LIMIT 100
2. 随机单设施聚合查问
1) 聚合计算某列的工夫距离的平均值
2) 反复1000次查问,每次查问设施随机指定
3) 查问工夫区间别离为:1天、7天、7天、1月,对应聚合工夫为1小时、1小时、7天,7天。
4) 统计查问耗时的最大值、最小值、平均值
5) SELECT AVG(col_1) FROM device_${random} WHERE ts >= ${tStart} and ts < ${tEnd} INTERVAL(${timeslot})
3. 随机多设施聚合查问
1) 聚合计算某列的工夫距离的总和
2) 反复1000次查问,每次查问设施约10000个
3) 查问工夫区间别离为:1天、7天、7天、1月,对应聚合工夫为1小时、1小时、7天,7天。
4) 统计查问耗时的最大值、最小值、平均值
5) SELECT SUM(col_1) FROM stable WHERE ts >= ${tStart} and ts < ${tEnd} AND device_id in (${deviceId_array}) INTERVAL(${timeslot})
能够看到,TDengine的空间占用只有Druid的60%(没有计算Druid应用的Deep storage)。 针对繁多设施的查问与聚和的响应工夫比Druid有倍数的晋升,尤其时间跨度较久时差距更显著(在十倍以上),同时Druid的响应工夫方差也较大。然而针对多子表的聚合操作,TDengine与Druid的区别便不再显著,能够说是各有优劣。
总之,TDengine与Druid在物联网数据方面的比照,前者的性能、资源应用方面均有较大当先。再联合TDengine装置部署配置上的便利性(咱们会波及到一些私有化利用的部署场景,这点对咱们来说十分重要),及相较于Apache社区其所提供的更牢靠与及时的商业服务,咱们最终决定将传感数据迁徙到TDengine中。
三、迁徙后的零碎
- 建表与迁徙
因为咱们零碎内接入的设施品种十分多,所以一开始数据存储便以大宽表的形式存储:50列double类型、20列binary类型、10列bool以及额定的几列通用列,同时还额定保护了一份记录了每列理论列名的映射表。这种存储模式在基于Druid的零碎中便曾经实现了,在TDengine中咱们也创立了同样构造的超级表,列名如: number_col1, number_col2, ..., number_col50, str_col1, str_col2, ..., str_col10
。
在本来的数据写入服务中,会将{"foo": 100, "bar": "foo"}
转换成 {"number_col1": 100, "str_col1": "foo"}
,同时记录一份 [foo=> number_col1, bar=>str_col1]
的映射关系(每一型号的设施共用雷同的映射),而后将解决后的数据写入Kafka集群中。
当初要将数据写入到TDengine中,也只须要基于本来要写入Kafka的数据来生成对应的insert SQL,再通过TAOSC写入TDengine即可,且在数据查问时也会主动从映射关系中读取对应的实在列名返回给调用方。这样对下层利用来说,输入输出的数据保障了对立且无需变动,同时即使咱们零碎频繁的减少新的设施类型,基本上也不再须要手动创立新的超级表。
基于TDengine后的零碎架构
当然期间也遇到了一些小问题,次要就是在依据设施建表时,某些前缀加设施惟一标识形成表名,但设施惟一标识外面可能会蕴含减号”-“这种特殊字符。对于过后的TDengine版本来说,这种非凡或保留字符是无奈作为表名或列名的,所以额定解决了一下。此外列名无奈辨别大小写也使得咱们本来“fooBar”这种驼峰形式的命名须要批改成“foo_bar”这种下划线分隔。不过TDengine 2.3.0.0之后反对了转义字符“`”后,这些问题就都失去了解决。
- 迁徙后成果
迁徙后,TDengine为咱们零碎里的各式各样的传感器提供了对立的数据存储服务,通过两头数据层的封装,咱们下层的业务根本无需批改便能够顺利地迁徙过去。相比于Druid须要部署各种各样的Server,TDengine仅须要部署DNode即可,也不再须要部署PG、ZK、Ceph等内部依赖。
迁徙后的利用接口响应工夫P99也从560毫秒左右升高到130毫秒(波及屡次外部RPC调用与DB的查问并不单纯示意TDengine查问响应工夫):
某历史数据查问接口响应工夫
某数据聚合接口响应工夫
对于开发人员来说,TDengine让咱们不须要再破费太多工夫与精力去钻研查问怎么样更高效(只有不间接应用数据列做过滤条件并指定正当的查问时间段后,大部分查问都能失去称心的响应工夫),能够更多地聚焦于业务性能实现上。同时咱们的运维同学们也得以从Druid的各个简单模块中解脱进去,在操作任何中间件时都不须要再对Druid的状况进行确认。
且值得一提的是,在理论业务环境中,以下面形容的形式创立多列的超级表,尽管会存在大量的空列,但得益于TDengine的优化,能达到恐怖的0.01的压缩率,简略计算下来大概须要3.67GB每亿条。另外一张超级表(约25列数据列)针对传感器数据进行独自建模(不会存在空列的状况),压缩率也有0.2,计算一下空间应用约合3.8GB每亿条。 这样看来应用宽表这种存储形式联合TDengine的弱小压缩能力也不会带来很多额定的硬件老本开销,但却能显著的升高咱们的保护老本。
四、将来布局
目前咱们基于TDengine次要还是存储传感器设施上传的数据,后续也打算将基于传感数据分析出的事件数据迁徙过去,甚至还打算将AI辨认算法剖析出的结构化数据也存储到TDengine中。 总之,经验了此次单干,咱们会把TDengine作为数据中台里重要的一种存储引擎应用,而非简略地存储传感器数据。将来,置信在涛思同学们的反对下,咱们能为客户提供更加优质的服务,打造物联网与人工智能利用的数字标杆。
作者简介
段雪林,北京升哲高级后端开发工程师,次要负责升哲灵思物联网中台的设计开发工作。
想理解更多TDengine的具体细节,欢送大家在GitHub上查看相干源代码。