时序数据库 Apache-IoTDB源码解析之前言(一)
IoTDB
是一款时序数据库,相关竞品有 Kairosdb
,InfluxDB
,TimescaleDB
等,主要使用场景是在物联网相关行业,如:车联网、风力发电、地铁、飞机监控等等,具体应用案例及公司详情可以查看:IoTDB在实际公司中的使用信息收集
IoTDB
模块主要分为Client
,JDBC
,Server
,TsFile
,Grafana
,Distribution
以及各种生态的连接器。整个系列的文章会从行业背景开始讲起,了解一个行业具体的使用场景,然后介绍 TsFile
是以什么样的格式来保存数据的,再介绍 Server
里怎样完成一次查询,最后在介绍一条完 整的 SQL
是怎样从 Client
使用 JDBC
到 Server
直至返回具体结果。如果有能力的话再介绍一下集群的一些内容和工作方式。
打一波广告本人专注车联网领域多年,现任四维智联架构师。目前正在参与 IoTDB 社区,有志同道合的同伴欢迎加微信:liutaohua001
欢迎大家访问 IoTDB 仓库,求一波 Star 。
这一章主要想聊一聊:
- 为什么重复造*,从物联网行业的数据特点到
IoTDB
的发展过程 - 这个*造的怎么样,
IoTDB
和竞品测试对比
时序数据
我个人理解时序数据是基于时间维度的同一个物体或概念的值构成的一个序列数据。在传统关系型数据库中,例如 MySQL
,我们通常会放置一个自增的 Id
列作为主键标识,如下:
Id | 人名 | 体温 | 测量时间 |
---|---|---|---|
1 | 张三 | 36.5 | 2020-02-06 9:00:00 |
2 | 李四 | 36.9 | 2020-02-06 9:00:00 |
3 | 王五 | 36.7 | 2020-02-06 9:00:00 |
4 | 张三 | 36.3 | 2020-02-06 9:30:00 |
5 | 张三 | 36.9 | 2020-02-06 11:00:00 |
上面的表结构就是一个时序数据,将表结构做个变形更容易理解:
时间戳 | 人名 | 体温 |
---|---|---|
1580950800 | 张三 | 36.5 |
1580950800 | 李四 | 36.9 |
1580950800 | 王五 | 36.7 |
1580952600 | 张三 | 36.3 |
1580958000 | 张三 | 36.9 |
如果把时间作为一个唯一键对齐展示,能够更像时序数据一些,这也是 IoTDB
中查询结果的展示方式:
时间戳 | 张三 | 李四 | 王五 |
---|---|---|---|
1580950800 | 36.5 | 36.9 | 36.7 |
1580952600 | 36.3 | NULL | NULL |
1580958000 | 36.9 | NULL | NULL |
这里可能会存在疑问就是假如人数是逐渐增加的,那么是动态创建列呢?还是提前创建足够多的列?这个问题等后面文章有机会继续介绍
物联网
物联网的特点是都会存在一个或多个设备,他们以各种各样的形式组织到一起,用来观测或记录同一时间里相同环境所产生的数据。下面的介绍中,使用由简单到复杂的数据逐步介绍在物联网行业中,通用的一些问题和方向。
1.基本存储
假如我是一个公司,对外播报北京、天津、上海三地的温度数据,从而实现盈利。
时间戳 | 北京 | 天津 | 上海 |
---|---|---|---|
1580950800 | 20.5 | 22.9 | 21.7 |
1580952600 | 20.5 | NULL | 22.9 |
1580958000 | 20.5 | 21.7 | 22.9 |
2.保证数据质量
数据保证的质量是多方面的,一步一步介绍。
2.1 更多设备
首先可以看到上面数据是存在 NULL
值的,这个 NULL
值有可能是因为当时设备所在的区域停电了,所以并没有办法上报当时的情况,这样客户如果想获取1580952600
这个时间戳对应的天津的数据的时候,肯定是拿不到了,所以传统思维上,我们应该增加一个容灾设备,保证一个设备在坏掉、停电、人为损坏等等的情况的时候,依然能够有数据上报回来。
基于这样的思想,以上的表结构就会变成:
时间戳 | 北京 1 | 北京 2 | 天津1 | 天津2 | 上海1 | 上海2 |
---|---|---|---|---|---|---|
1580950800 | 20.5 | 20.9 | 21.7 | 20.9 | 20.7 | 21.7 |
1580952600 | 21.5 | 21.0 | NULL | 21.7 | 21.7 | 21.7 |
1580958000 | 22.5 | 22.7 | 22.9 | 22.7 | NULL | NULL |
2.2 更高采样频率
这时候依然存在问题, 1580958000
这一刻两个设备都没有数据,有可能是放置设备的区域同时出现了断网或者断电,这种情况下,我们可以采用提高采集数据的频率或者补传数据来解决(补传暂不讨论)。
我们将每天数据分为3组,每组采样3次,间隔为1个小时,假如时间分布为:上午(7、8、9)、中午(12、13、14)、下午(18、19、20)。当增加了采样频率之后,即便某一刻出现了 NULL 数据,我们也可以采用临近时间做为补充。为了方便对应,下表数据中增加时间点列辅助查看。
时间点 | 时间戳 | 北京 1 | 北京 2 | 天津1 | 天津2 | 上海1 | 上海2 |
---|---|---|---|---|---|---|---|
7点 | 1580943600 | 20.5 | 20.9 | 21.7 | 20.9 | 20.7 | 21.7 |
8点 | 1580947200 | 21.5 | 21.0 | NULL | 21.7 | 21.7 | 21.7 |
9点 | 1580950800 | 22.5 | 22.7 | 22.9 | 22.7 | NULL | NULL |
12点 | 1580961600 | 20.5 | 20.9 | 21.7 | 20.9 | 20.7 | 21.7 |
13点 | 1580965200 | 21.5 | 21.0 | NULL | 21.7 | 21.7 | 21.7 |
14点 | 1580968800 | 22.5 | 22.7 | 22.9 | 22.7 | NULL | NULL |
18点 | 1580983200 | 20.5 | 20.9 | 21.7 | 20.9 | 20.7 | 21.7 |
19点 | 1580986800 | 21.5 | 21.0 | NULL | 21.7 | 21.7 | 21.7 |
20点 | 1580990400 | 22.5 | 22.7 | 22.9 | 22.7 | NULL | NULL |
可以看到经过各种各样的需求之后,上传的数据是成倍增长的,不难想象如果这个温度数据希望精准的获取到每个县城的温度,那么中国有 2854 个县城 * 2 个温度设备 * 9 条数据 = 1 天产生的数据总量 = 51372 条,那么一个月就是 1541160 条。
数据实时性及总量
假如上面的数据我们继续提高频率到每1分钟每个设备上报一次,那么数据量就会成为 2854 * 2 * 60 * 24 = 246585600 条/天。
在这样的数据量下,实时插入实时做一些聚合计算,应该传统数据库就有些处理不过来了。
IoTDB 的前身
某公司在实际业务中,20 万设备保存了 3 年的数据,TB级别的数据使得 Oracle 被拖的根本吃不消。关键的问题点还不仅仅是存量数据大,新增数据依然以非常快的速度在增长。后来公司联系到了 IoTDB
的第一批开发者,但是当时的方案还是基于 Cassandra 来做设计,当时规划了 5 台机器的集群,性能刚满足,但随着时间推移设备总量在增加,业务系统的查询请求量在增加。Cassandra 在经过大量的努力之后,最后发现如果再改可能就需要大面积的重构 Cassandra 数据的代码了,最终决定重新设计一个存储方式,来解决物联网场景下的时序数据高效写入、低延迟读取、高压缩比持久化。
PS: 以上都是黄向东 (IoTDB PPMC) ,在 meetup 中讲到的,我只是在脑中存留了一部分,具体的细节大家可以到 IoTDB 社区交流。
性能对比
测试工具使用的是由清华大学大数据实验室开发的iotdb-benchmark
1.写入性能对比
数据集2 | 客户端 | 存储组 | 设备 | 变量 | batchsize | LOOP | 数据量 | 写入速度(point/s) | 硬盘数据大小 |
---|---|---|---|---|---|---|---|---|---|
IoTDB | 10 | 10 | 10 | 10 | 1000 | 1000000 | 1E+11 | 24750321.93 | 38306092 |
InfluxDB | 10 | 10 | 10 | 10 | 1000 | 1000000 | 1E+11 | 304682932 | |
TimescaleDB | 10 | 10 | 10 | 10 | 1000 | 1000000 | 1E+11 | 737689.22 | 1610219064 |
数据集1 | 客户端 | 存储组 | 设备 | 变量 | batchsize | LOOP | 数据量 | 写入速度(point/s) | 硬盘数据大小 |
---|---|---|---|---|---|---|---|---|---|
IoTDB | 10 | 10 | 10 | 10 | 1000 | 100000 | 10000000000 | 20706345.15 | 3599732 |
InfluxDB | 10 | 10 | 10 | 10 | 1000 | 100000 | 10000000000 | 1729907.81 | 30546560 |
TimescaleDB | 10 | 10 | 10 | 10 | 1000 | 100000 | 10000000000 | 715857 | 161026468 |
KairosDB | 10 | 10 | 10 | 10 | 10000 | 10000 | 10000000000 | 24924.97 | 76263380 |
上面一组数据可以看出写入性能高于同款数据库10倍有余,单机写入速度高达到每秒2千万。且硬盘占用是最小的,这在数据比较大的线上业务中,可能每个月会差出来 1 到 2 块硬盘。
2. 查询性能对比
原始数据查询
客户端 | 存储组 | 设备 | 序列-数据量 | 变量 | 查询点数 | LOOP | 速度(point/s) | AVG | MIN | |
---|---|---|---|---|---|---|---|---|---|---|
IoTDB | 10 | 10 | 10 | 1.00E+09 | 1 | 1000000 | 100 | 12942984.85 | 740.27 | 457.04 |
InfluxDB | 10 | 10 | 10 | 1.00E+09 | 1 | 1000000 | 100 | 1779606.4 | 5591 | 4666.39 |
TimescaleDB | 10 | 10 | 10 | 1.00E+09 | 1 | 1000000 | 100 | 3781467.52 | 2345.69 | 1193.78 |
聚合数据查询
客户端 | 存储组 | 设备 | 序列-数据量 | 变量 | LOOP | 范围 | 速度(point/s) | AVG | MIN | |
---|---|---|---|---|---|---|---|---|---|---|
IoTDB-1 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.0001 | 49.75 | 27.87 | 18.03 |
IoTDB-2 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.001 | 49.75 | 49.14 | 19.87 |
IoTDB-3 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.01 | 49.76 | 48.69 | 22.32 |
IoTDB-4 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.1 | 48.68 | 99.14 | 25.56 |
IoTDB-5 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 1 | 14 | 595.61 | 45.54 |
InfluxDB-1 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.0001 | 234.32 | 40.28 | 21.63 |
InfluxDB-2 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.001 | 28.88 | 341.9 | 238.1 |
InfluxDB-3 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.01 | 3.07 | 3226.87 | 2664.86 |
TimescaleDB-1 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.0001 | 42.39 | 220.57 | 120.5 |
TimescaleDB-2 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.001 | 5.8 | 1502.9 | 754.15 |
TimescaleDB-3 | 10 | 10 | 10 | 1.00E+09 | 1 | 100 | 0.01 | 1.02 | 9711.55 | 7148.69 |
3. 对比图
整体来看 IoTDB
无论在写入、原始数据查询还是聚合查询,都几乎是10倍的性能于竞品数据库,而且硬盘占用又小于同款数据库10倍,那么 IoTDB
是怎样完成如此高的压缩比、如此恐怖的写入速度、如此高效的查询呢?欢迎继续关注。。。