本文根据演讲视频以及PPT整理而成。
本文将主要围绕以下四个方面进行分享:
- 时序数据与时序数据库
- 时序数据库的演变
- 时序数据库对比
- 总结
一、时序数据与时序数据库
什么是时序数据库?
按照维基百科解释,时间序列数据库(TSDB)是一个为了用于处理时间序列数据而优化的软件系统,其按时间数值或时间范围进行索引。
时序数据库增长趋势
时序数据库从2014年开始就被DB-Engine列入了单独的数据库类别进行统计。下图所示的是在过去的24个月内所有数据库类别在DB-Engine上的增长趋势,可以看出时序数据库的增长趋势排在第一位。
时序数据举例
列举一个时序数据的例子,比如在张北有一些风力发电机组,这些风力发电机都具有能够代表自身唯一性的标签(Tag),比如生产厂商、风场、型号以及自身ID等,而每个风机都会产生一些指标(Metrics),比如当前的运转功率、当前风速等。随着时间,这些风机会产生新的数据,这些数据就是时序数据。
时序数据库的应用场景
如下图所示的是一些时序数据库的应用场景,第一个就是证券交易场景,大家所熟知的K线图就是一个比较标准的时间序列。又比如像广告数据展示,网站PV/UV,这些对运营人员很重要的时序数据。智能手环、智能手表等可穿戴设备可以采集个人健康数据,比如睡眠信息、运动信息等,并按照时间顺序将其发送到云端,经过一系列计算,最终为用户提供一些建议和提示。除上述比较常见的时序数据之外,还有气温变化情况、工业传感器数据、服务器监控数据、网络设备监控数据等。最后,还有车联网数据场景,如今新能源汽车越来越多,大部分汽车都支持通过APP直接管理,它们会时时刻刻地产生车辆的GPS、续航、安全事件等数据,云端可以利用这些数据进行分析处理,用户也可以用来查看车辆情况。
时序数据库的特性
时序特性:对于时序数据的时序特性而言,每条数据都会有一个时间戳,而按照要求的不同,时间戳在精度上有不同。通常而言,大多数时序数据以秒为单位,而在一些工业上会有以毫秒或者纳秒为单位的情况。数据的采样频率主要分为两种,一种是以可感知的周期频率来采集的,另外一种则是以不可感知的网站PV/UV进行离散型采样,这是因为虽然用户访问可以被提前预测,但是精确度不会很高。
数据特性:对于时序数据的数据特性而言,数据都是顺序可追加的,也是多维可关联的,它们具有不同的指标。近期的时序数据往往是大家所关注的,访问频率会比较高,而时间较远的数据所带来的价值则会随着时间的推移而逐渐降低,因此时序数据也存在冷热归档的特性。此外,时序数据也会包含一些具体的数值、状态和事件。
CRUD特性:而对于时序数据库而言,也会有一些CRUD的特性。时序数据库的读写有点类似LSM数据库的特点,写操作的频率远远大于读,并且存在一定的时间窗口。此外,时序数据库通常极少更新数据,存在一定的覆盖写,并且支持批量删除操作。而随着业务对于技术的要求越来越高,因此时序数据库也需要支持高可用、高可靠以及可伸缩等特性。通常情况下,时序数据库不具备事务能力。
**
二、时序数据库的演变**
如下图所示的是随着时间而诞生的一些比较具有代表性的时序数据库。最早诞生的时序数据库就是RRDTool,RRD是Round-Robin Database的意思,它属于基于平板文件的简单存储。后来,伴随着大数据和Hadoop生态的发展,OpenTSDB,KairosDB这些基于Hadoop之上通用存储而专门构建的时间序列数据库开始出现,它可以按时间间隔高效地存储和处理数据。 而由于OpenTSDB基于HBase这样一个通用的NoSQL存储,虽然可以解决时序相关的一些场景问题,但无法满足对于成本和性能要求更高的场景。而随着Docker、Kubernetes、微服务等技术的发展,以及对于IoT的发展预期越来越强烈。在数据随着时间而增长的过程中,时间序列数据成为增长最快的数据类型之一。高性能、低成本的垂直型时序数据库开始诞生,以InfluxDB为代表的具有时序特征的数据存储引擎逐步引领市场。而在后来的2015年,FaceBook开源了Gorilla,在时序领域应用比较广泛。在2017年,微软发布了Azure Series Insights。2018年,Amazon发布了Timestream预览版。由此可以看出,时序数据库的需求与应用愈发广泛。
**
可以简单概括时序数据库发展演变历程:**
- 第一代时序数据库:基于平板文件的简单存储。代表:RRDTools,Graphite。RRD(Round Robin Database)。
- 第二代时序数据库:依赖与Hadoop生态的通用分布式存储系统。代表:OpenTSDB,KairosDB
- 第三代时序数据库:对高性能,低成本有强需求,需要针对时序领域特别专门设计。代表:InfluxDB
- 特例:TimescaleDB。基于PostgreSQL关系数据库构建。
时序数据库发展现状
如下图所示的是时序数据库在不同领域中比较具有代表性的产品,主要分为了开源、公有云、学术以及商业&工业四大领域。
三、时序数据库的对比
OpenTSDB
OpenTSDB以HBase作为底层存储,对应的会有一些TSD,也就是OpenTSDB的主要进程。TSD互相是对等结构,不存在Master-Slave主从关系,在向OpenTSDB写数据之前需要做好负载均衡。
下图所示的是OpenTSDB存储数据的逻辑关系,以MySQL的指标数据为例。最前面是指标Metric,接着是Timestamp,需要注意的是这里的Timestamp和HBase以及HFile上的Timestamp是没有任何关系的。HFlie里面的Timestamp是用来MVCC或TTL等来使用的,而时序领域所使用的Timestamp主要指的是数据产生的时间或落盘的时间。在Timestamp之后是指标的Value以及Tag,也就是DataSource。以上是逻辑上的关系,实际情况中会因存储的不同有所变化。
对于每条数据而言,都会有指标名称和对应的DataSource,如果直接把这些数据存储到HBase中则会造成极大的存储浪费,这是因为字符串存储占用的空间比数值类型多很多。因此,OpenTSDB对应的解决办法是将每一个指标都编码成如下图所示的数值类型,进而减少存储占用,而将对应的Mapping映射存储一张全局映射表中。这样做的优势是相对节省存储,而缺点则是由于全局映射关系,使得对于硬件的要求有所提升。
下图所示的是HFile结构。HFile以Block作为最小存储单位,主要的存储类型有Index Block以及Data Block等。对于数据部分而言,每个Data Block可以拆分成一个个Key-Value对象,再进一步细化则会有偏移量KeyLength和ValueLength以及字符串对象KeyBytes和ValueBytes。而对于KeyBytes再进一步细分,可以分为RowLength、RowBytes、ColumnFamilyLength、ColumnFamily、TimeStamp以及KeyType等对象。很多偏移量字段对于HBase而言有用而对于时序领域而言这些偏移量字段没有任何意义,但是因为HFlie的设计原因,所以不得不造成存储甚至性能上的浪费。
OpenTSDB做了一些优化。首先,OpenTSDB的一行数据的Timestamp会以小时为粒度,然后将小时中的每一秒设置到列上,用逻辑上的一行来存储一小时的数据,用偏移量替代原始数值类型,以此来减少存储的占用。其次,对数据源(Tag)和指标(Metric)使用全局编码。但是经过全局编码后依然存在数据冗余。再次,还是由于通用存储的原因,HFile主要解决的还是Key-Value等场景,并没有针对时序场景特定压缩,因此也会造成存储和性能浪费。最后,OpenTSDB是时序数据库中少数几个没有实现多维查询的数据库。
Druid
Druid是一个不折不扣的列式数据库,所以每一列都会独立存储。Druid和HBase的处理方式一样,都是采用编码字典对标签值进行编码,将String类型的标签值编码成int值。但和HBase不一样的是,Druid编码是局部编码,Druid和HBase都采用LSM结构,数据先写入内存再flush到数据文件,Druid编码是文件级别的,局部编码可以有效减小对内存的巨大压力。
Druid的特性
Druid的这种列式存储模式还有如下好处:
- 有数据类型概念,数据压缩率高。每列独立存储,可以针对每列进行压缩,而且可以为每列设置对应的压缩策略,比如时间列、int、fload、double、string都可以分别进行压缩,压缩效果更好。
- 支持多维查找。Druid为Datasource的每个列分别设置了Bitmap索引,利用Bitmap索引实现多维查找。
Druid存储模型也有一些问题:
- 数据依然存在冗余。和OpenTSDB一样,Tags存在大量的冗余。这是因为NoSQL的列存储也需要一些指标来记录列的信息。
- 指定数据源的范围查找并没有OpenTSDB高效。这是因为Druid会将数据拆成多列,每列在查询时走Bitmap索引,再最后使用与操作找到满足条件的行号,这个过程需要一定的开销。而OpenTSDB中直接可以根据数据源拼成rowkey,在索引block按B+树方式进行查找,效率更高。
InfluxDB
如下图所示的是InfluxDB逻辑架构图。InfluxDB其内部会有一些Rentention Policy,负责配置数据Duration,ShardGroup Duration和副本数量。对于Shard Group而言,可以简单地按字面意思理解,Influx Cluster会在Shard Group内创建一系列的Shard。Shard Group是一个逻辑概念。对于Shard,开源版本Shard数量默认为1,商业版会按照数据节点数以及所需的数据副本数量创建。N个数据节点X个副本,则创建N/X个Shard。Shard就是数据分区,具体形式就是TSM Engine,其主要负责数据编码,数据读写。TSM是LSM的变形,因此相似于LSM的数据库,也具有Cache,WAL,Compaction,Flush等等这些组件与操作,只不过TSM在LSM的基础之上进行了一些优化。
如下图所示的是InfluxDB的时序数据在内存中的组织形式,时序数据写入InfluxDB内存后会按照SeriesKey进行组织。可以认为SeriesKey就是一个数据源,而Field就是采集的指标。比如SeriesKey是一部iPhone,Field包含经纬度,高度,行走步数等。具体的数值会借助偏移量的方式进行存储。
如下图所示的是TSM文件的最粗粒度的结构。在InfluxDB中,数据以Block为最小单位存储在文件中,如图所示文件结构主要包含Data Block、Index Block以及最后的Footer。
将上图进一步细化的结果如下图所示。在TSM中,索引做到了文件级别,索引的实现类似于B+树,而且数据块和索引结构存储在同一个文件中。InfluxDB中有数据类型的概念,同一个Block中只会存储同一种Key的数据。
SM文件的详解解释如下:
- Type:表示该SeriesKey对应的时间序列的数据类型,数值数据类型通常为int、long、float以及double等。不同的数据类型对应不同的编码方式。
- Length:len(Timestamps),用于读取Timestamps区域数据,解析Block。时序数据的时间值以及指标值在一个Block内部是按照列式存储的:所有的时间值存储在一起,所有的指标值存储在一起。使用列式存储可以极大提高系统的压缩效率。
- Timestamps:时间值存储在一起形成的数据集,通常来说,时间序列中时间值的间隔都是比较固定的,比如每隔一秒钟采集一次的时间值间隔都是1s,这种具有固定间隔值的时间序列压缩非常高效,TSM采用了Facebook开源的Geringei系统中对时序时间的压缩算法:delta-delta编码。
- Values:指标值存储在一起形成的数据集,同一种Key对应的指标值数据类型都是相同的,由Type字段表征,相同类型的数据值可以很好的压缩,而且时序数据的特点决定了这些相邻时间序列的数据值基本都相差不大,因此也可以非常高效的压缩。需要注意的是,不同数据类型对应不同的编码算法。
对于InfluxDB而言,在1.3版本之前,所有的索引都做到了内存里。内存索引的好处就是查询速度优于其他持久化存储介质,但是缺点是索引的容量会受到限制。如果具有海量的标签,那么索引量非常大,可能内存中无法存放;其次,如果发生宕机,那么在Startup阶段,就需要重新扫描所有的TSM文件,并在内存中重新排列这些索引。正是因为上述问题,使得索引对于资源的消耗过高,所以在1.3版本之后,InfluxDB实现了基于磁盘的索引。虽然这样的磁盘索引能够解决大部分场景,但是因为存储占用的关系,所以也只实现了Tag到SeriesKey的映射,没有实现Tag到SeriesKey + FieldKey + Timestamp的映射。
Prometheus
与其他时序数据库不同,Prometheus不仅包含存储,更是一个生态系统,提供了几乎整个运维领域的全部组件,提供了服务发现、数据采集服务、存储相关服务、报警相关组件等,并且提供了Web UI界面。Prometheus的优点是其提供了一整套监控解决方案,不需要运维人员去逐个拼装开源组件。此外,Prometheus还提供了一套标准,研发人员可以根据协议来替换组件,提供了更好的灵活性。
Prometheus提供了如下的关键功能
多维数据模型:Metric,Labels
灵活的查询语言:PromQL, 在同一个查询语句,可以对多个 Metrics 进行乘法、加法、连接、取分数位等操作。
可独立部署,拆箱即用,不依赖分布式存储
通过Http pull的采集方式
通过push gateway来做push方式的兼容
通过静态配置或服务发现获取监控项
支持图表和dashboard等多种方式
TimeScaleDB
TimeScaleDB是一个比较特殊的例子,它是基于关系型数据库实现的时序数据库。关系数据库通常默认的引擎所采用的都是B+树,而对于B+树而言,随着数据量不断写入,其内部节点的排序会造成很大压力。从用户的视角,TimescaleDB对外暴露的是一个叫做hypertables的表,其是一个抽象概念。随着数据的不断写入,其将会形成多个物理上的子表,使得每个子表上的数据不至于过高,解决了单表数据量的问题。
TimeScaleDB的特性
基于标准的PG,未对PG存储层做任何修改
时序数据表的透明自动分区特性
提供了若干面向时序数据应用场景的特殊SQL接口(使用时空UDF实现)
针对时序数据的写入和查询对PostgreSQL的Planner进行扩展和优化
面向时序数据表的定制化并行查询
除了上述特性之外,TimeScaleDB还具有时序数据表的透明自动分区功能。该功能所要实现的目标如下:
随着数据写入的不断增加,将时序数据表的数据分区存放,保证每一个分区的索引维持在一个较小规模,从而维持写入的性能。
基于时序数据的查询场景,自动分区时以时序数据的时间戳为分区键,从而确保查询时可以快速定位到所需的数据分区,保证查询性能。
分区过程对用户透明,从而达到Auto-Scalability的效果。
借助透明化自动分区的特性,根据官方的测试结果,在同样的数据量级下,TimescaleDB的写入性能与PG的传统单表写入场景相比,即使随着数量级的不断增大,性能也能维持在一个比较稳定的状态。
TimeScaleDB的特性
对于TimeScaleDB的优势而言,首先,其100%继承自PostgreSQL的生态,且由于完整支持SQL,对于未接触过时序数据库的用户有很高吸引力;其次,因为TimeScaleDB基于PG,所以也具有灵活的JSON格式支持,无需在JSON解析上浪费计算资源;并且TimeScaleDB还可以实现快速的复杂查询,也继承了PostgreSQL的高品质与稳定性优势,还具有强ACID支持。
对于TimescaleDB的缺陷而言,因为其是PostgreSQL的一个延伸,因此不能像从内核和存储层面针对时序数据库的场景进行极致优化。虽然官网上宣传能够支持集群模式,但是仍旧处于开发中,目前从产品的架构来看仍然是一个单机库,不能发挥分布式技术的优势。而且数据虽然自动分区,但是由于时间戳决定分区,比较容易形成I/O热点。而在功能层面,面向时序数据库场景的特性有限,更像是一个传统OLTP+部分时序特性。在NoSQL具有优势的简单快速查询场景,非结构化数据,存储成本和离线分析场景方面,TimescaleDB不具有优势。
阿里云时序时空数据库TSDB
阿里云时序时空数据库TSDB的发展演变经历了三个阶段,最开始1.0阶段主要基于OpenTSDB,底层实现了双引擎——HBase和HiStore。而随着阿里业务场景对于时序数据库不断提出新的挑战,在2.0阶段中,阿里云就将OpenTSDB引擎换成了自研的TSDB引擎,实现了OpenTSDB所没有支持的倒排索引、基于时序场景的特殊编码、分布式流计算聚合数据等功能和特性。而随着业务场景以及整个公司生态的完善,阿里云时序时空数据库TSDB实现了边缘计算和云的一体化,实现了TSQL,兼容了Prometheus生态,并且除了能够支持时序数据引擎之外,还能够支持时空数据引擎。
阿里云时序时空数据库TSDB的特性
支持两种存储模型。时间线的ID + TS和继承自OpenTSDB的Metric + TS + Tag。
双层Sharding结构。先基于时间分区索引,并在存储层做Salting,避免时间分区热点。
基于Coprocessor实现列合并压缩算法,在通用ZSTD+DIFF编码的基础上进一步将存储空间减小至压缩前的1/3。
支持分布式流聚合计算。
工作负载优化:读写线程池分离,大小查询资源隔离,超时大查询Skip,限流及状态管理。
四、总结
对于时序数据库而言,主要存在三种流派。第一种是以Timescale为代表的标准SQL,其能够极大地降低入门门槛,也能解决时序问题,但是体量上不占优势;第二种是以OpenTSDB为代表的基于通用NoSQL的时序存储引擎;最后一种则是为了应对时序场景的业务挑战而单独设计的以InfluxDB为代表NoSQL数据库。
这三种时序数据库的主要区别如下所示:
SQL vs NoSQL
固定Schema vs 动态Schema
通用存储 vs 时序专用存储
单机 vs 分布式
结构化数据 vs 非结构化数据
低学习成本 vs 相对高的学习成本