时序数据库的选择?

做者:网易云
连接:https://www.zhihu.com/question/50194483/answer/428449003
来源:知乎
著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。

基于这个问题,推荐我厂范欣欣同窗的一篇文章,这篇文章笔者将会分别针对OpenTSDB、Druid、InfluxDB以及Beringei这四个时序系统中的时序数据模型设计进行介绍。但愿对题主有所帮助,原文以下:算法


时序数据库技术体系中一个很是重要的技术点是时序数据模型设计,不一样的时序系统有不一样的设计模式,而不一样的设计模式对时序数据的读写性能、数据压缩效率等各个方面都有很是重要的影响。这篇文章笔者将会分别针对OpenTSDB、Druid、InfluxDB以及Beringei这四个时序系统中的时序数据模型设计进行介绍。数据库

 

在详细介绍时序数据模型以前,仍是有必要简单回顾一下时序数据的几个基本概念,以下图所示:设计模式

 

上图是一个典型的时序数据示意图,由图中能够看出,时序数据由两个维度坐标来表示,横坐标表示时间轴,随着时间的不断流逝,数据也会源源不断地吐出来;和横坐标不一样,纵坐标由两种元素构成,分别是数据源和metric,数据源由一系列的标签(tag,也称为维度)惟一表示,图中数据源是一个广告数据源,这个数据源由publisher、advertiser、gender以及country四个维度值惟一表示,metric表示待收集的数据源指标。一个数据源一般会采集不少指标(metric),上图中广告数据源就采集了impressions、clicks以及revenue这三种指标,分别表示广告浏览量、广告点击率以及广告收入。缓存

 

看到这里,相信你们对时序数据已经有了一个初步的了解,能够简单的归纳为:一个时序数据点(point)由datasource(tags)+metric+timestamp这三部分惟一肯定。然而,这只是逻辑上的概念理解,那具体的时序数据库究竟是如何将这样一系列时序数据点进行存储的呢?下文笔者针对OpenTSDB、Druid、InfluxDB以及Beringei四种系统进行介绍。架构

 

OpenTSDB(HBase)时序数据存储模型app

 

OpenTSDB基于HBase存储时序数据,在HBase层面设计RowKey规则为:metric+timestamp+datasource(tags)。HBase是一个KV数据库,一个时序数据(point)若是以KV的形式表示,那么其中的V必然是point的具体数值,而K就天然而然是惟一肯定point数值的datasource+metric+timestamp。这种规律不只适用于HBase,还适用于其余KV数据库,好比Kudu。性能

 

既然HBase中K是由datasource、metric以及timestamp三者构成,如今咱们能够简单认为rowkey就为这三者的组合,那问题来了:这三者的组合顺序是怎么样的呢?优化

 

首先来看哪一个应该排在首位。由于HBase中一张表的数据组织方式是按照rowkey的字典序顺序排列的,为了将同一种指标的全部数据集中放在一块儿,HBase将将metric放在了rowkey的最前面。假如将timestamp放在最前面,同一时刻的数据必然会写入同一个数据分片,没法起到散列的效果;而若是将datasource(即tags)放在最前面的话,这里有个更大的问题,就是datasource自己由多个标签组成,若是用户指定其中部分标签查找,并且不是前缀标签的话,在HBase里面将会变成大范围的扫描过滤查询,查询效率很是之低。举个上面的例子,若是将datasource放在最前面,那rowkey就能够表示为publisher=ultrarimfast.com&advertiser:google.com&gender:Male&country:USA_impressions_20110101000000,此时用户想查找20110101000000这个时间点全部发布在USA的全部广告的浏览量,即只根据country=USA这样一个维度信息查找指定时间点的某个指标,并且这个维度不是前缀维度,就会扫描大量的记录进行过滤。ui

 

肯定了metric放在最前面以后,再来看看接下来应该将datasource放在中间呢仍是应该将timestamp放在中间?将metric放在前面已经能够解决请求均匀分布(散列)的要求,所以HBase将timestamp放在中间,将datasource放在最后。试想,若是将datasource放在中间,也会遇到上文中说到的后缀维度查找的问题。google

 

所以,OpenTSDB中rowkey的设计为:metric+timestamp+datasource,好了,那HBase就能够只设置一个columnfamily和一个column。那问题来了,OpenTSDB的这种设计有什么问题?在了解设计问题以前须要简单看看HBase在文件中存储KV的方式,即一系列时序数据在文件、内存中的存储方式,以下图所示:

 

 

上图是HBase中一个存储KeyValue(KV)数据的数据块结构,一个数据块由多个KeyValue数据组成,在咱们的事例中KeyValue就是一个时序数据点(point)。其中Value结构很简单,就是一个数值。而Key就比较复杂了,由rowkey+columnfamily+column+timestamp+keytype组成,其中rowkey等于metric+timestamp+datasource。

 

问题一:存在不少无用的字段。一个KeyValue中只有rowkey是有用的,其余字段诸如columnfamily、column、timestamp以及keytype从理论上来说都没有任何实际意义,但在HBase的存储体系里都必须存在,于是耗费了很大的存储成本。

 

问题二:数据源和采集指标冗余。KeyValue中rowkey等于metric+timestamp+datasource,试想同一个数据源的同一个采集指标,随着时间的流逝不断吐出采集数据,这些数据理论上共用同一个数据源(datasource)和采集指标(metric),但在HBase的这套存储体系下,共用是没法体现的,所以存在大量的数据冗余,主要是数据源冗余以及采集指标冗余。

 

问题三:没法有效的压缩。HBase提供了块级别的压缩算法-snappy、gzip等,这些通用压缩算法并无针对时序数据进行设置,压缩效率比较低。HBase一样提供了一些编码算法,好比FastDiff等等,能够起到必定的压缩效果,可是效果并不佳。效果不佳的主要缘由是HBase没有数据类型的概念,没有schema的概念,不能针对特定数据类型进行特定编码,只能选择通用的编码,效果可想而知。

 

问题四:不能彻底保证多维查询能力。HBase自己没有schema,目前没有实现倒排索引机制,全部查询必须指定metric、timestamp以及完整的tags或者前缀tags进行查询,对于后缀维度查询也勉为其难。

 

虽然说有这样那样的问题,可是OpenTSDB仍是针对存储模型作了两个方面的优化:

 

优化一:timestamp并非想象中细粒度到秒级或毫秒级,而是精确到小时级别,而后将小时中每一秒设置到列上。这样一行就会有3600列,每一列表示一小时的一秒。这样设置听说能够有效的取出一小时整的数据。

 

优化二:全部metrics以及全部标签信息(tags)都使用了全局编码将标签值编码成更短的bit,减小rowkey的存储数据量。上文分析HBase这种存储方式的弊端是说道会存在大量的数据源(tags)冗余以及指标(metric)冗余,有冗余是吧,那我就搞个编码,将string编码成bit,尽最大努力减小冗余。虽然说这样的全局编码能够有效下降数据的存储量,可是由于全局编码字典须要存储在内存中,所以在不少时候(海量标签值),字典所需内存都会很是之大。

 

上述两个优化能够参考OpenTSDB这张经典的示意图:

 

 

Druid时序数据存储模型设计

 

和HBase和Kudu这类KV数据库不一样,Druid是另外一种玩法。Druid是一个彻彻底底的列式存储系统,没有HBase的主键。上述时序数据在Druid中表示是下面这个样子的:

 

 

Druid是一个列式数据库,因此每一列都会独立存储,好比Timestamp列会存储在一块儿造成一个文件,publish列会存储在一块儿造成一个文件,以此类推。细心的童鞋就会说了,这样存储,依然会有数据源(tags)大量冗余的问题。针对冗余这个问题,Druid和HBase的处理方式同样,都是采用编码字典对标签值进行编码,将string类型的标签值编码成int值。但和HBase不同的是,Druid编码是局部编码,Druid和HBase都采用LSM结构,数据先写入内存再flush到数据文件,Druid编码是文件级别的,局部编码能够有效减少对内存的巨大压力。除此以外,Druid的这种列式存储模式还有以下好处:

 

1. 数据存储压缩率高。每列独立存储,能够针对每列进行压缩,并且能够为每列设置对应的压缩策略,好比时间列、int、fload、double、string均可以分别进行压缩,压缩效果更好。

 

2. 支持多维查找。Druid为datasource的每一个列分别设置了Bitmap索引,利用Bitmap索引能够有效实现多维查找,好比用户想查找20110101T00:00:00这个时间点全部发布在USA的全部广告的浏览量,能够根据country=USA在Bitmap索引中找到要找的行号,再根据行号定位待查的metrics。

 

然而,这样的存储模型也有一些问题:

 

问题一:数据依然存在冗余。和OpenTSDB同样,tags存在大量的冗余。

 

问题二:指定数据源的范围查找并无OpenTSDB高效。这是由于Druid会将数据源拆开成多个标签,每一个标签都走Bitmap索引,再最后使用与操做找到知足条件的行号,这个过程须要必定的开销。而OpenTSDB中直接能够根据数据源拼成rowkey,查找走B+树索引,效率必然会更高。

 

InfluxDB时序数据存储模型设计

 

相比OpenTSDB以及Druid,可能不少童鞋对InfluxDB并不特别熟悉,然而在时序数据库排行榜单上InfluxDB倒是遥遥领先。InfluxDB是一款专业的时序数据库,只存储时序数据,所以在数据模型的存储上能够针对时序数据作很是多的优化工做。

 

为了保证写入的高效,InfluxDB也采用LSM结构,数据先写入内存,当内存容量达到必定阈值以后flush到文件。InfluxDB在时序数据模型设计方面提出了一个很是重要的概念:seriesKey,seriesKey实际上就是datasource(tags)+metric,时序数据写入内存以后按照seriesKey进行组织:

 

 

内存中实际上就是一个Map:<SeriesKey, List<Timestamp|Value>>,Map中一个SeriesKey对应一个List,List中存储时间线数据。数据进来以后根据datasource(tags)+metric拼成SeriesKey,再将Timestamp|Value组合值写入时间线数据List中。内存中的数据flush的文件后,一样会将同一个SeriesKey中的时间线数据写入同一个Block块内,即一个Block块内的数据都属于同一个数据源下的一个metric。

 

这种设计咱们认为是将时间序列数据按照时间线挑了出来。先来看看这样设计的好处:

 

好处一:同一数据源的tags再也不冗余存储。一个Block内的数据都共用一个SeriesKey,只须要将这个SeriesKey写入这个Block的Trailer部分就能够。大大下降了时序数据的存储量。

 

好处二:时间序列和value能够在同一个Block内分开独立存储,独立存储就能够对时间列以及数值列分别进行压缩。InfluxDB对时间列的存储借鉴了Beringei的压缩方式,使用delta-delta压缩方式极大的提升了压缩效率。而对Value的压缩能够针对不一样的数据类型采用相同的压缩效率。

 

好处三:对于给定数据源以及时间范围的数据查找,能够很是高效的进行查找。这一点和OpenTSDB同样。

 

细心的同窗可能会问了,将datasource(tags)和metric拼成SeriesKey,不是也不能实现多维查找。确实是这样,不过InfluxDB内部实现了倒排索引机制,即实现了tag到SeriesKey的映射关系,若是用户想根据某个tag查找的话,首先根据tag在倒排索引中找到对应的SeriesKey,再根据SeriesKey定位具体的时间线数据。

 

InfluxDB的这种存储引擎称为TSM,全称为Timestamp-Structure Merge Tree,基本原理相似于LSM。后期笔者将会对InfluxDB的数据写入、文件格式、倒排索引以及数据读取进行专题介绍。

 

InfluxDB能够称为真正的时序数据库,存储引擎称为TSM,基本架构相似于LSM。数据按照key组织,InfluxDB中key由维度集合加上某一个列值名构成,全部属于该维度集合加列值的时间序列数值组成的一个集合就挂在该key下。这样,维度列值就再也不须要冗余存储,并且timestamp和point点可使用列式存储,独立压缩。InfluxDB的文件格式以下图所示:

 

Beringei时序数据存储模型设计

 

Beringei是今年Facebook开源的一个时序数据库系统。InfluxDB时序数据模型设计很好地将时间序列按照数据源以及metric挑选了出来,解决了维度列值冗余存储,时间列不能有效压缩的问题。但InfluxDB没有很好的解决写入缓存压缩的问题:InfluxDB在写入内存的时候并无压缩,而是在数据写入文件的时候进行对应压缩。咱们知道时序数据最大的特色之一是最近写入的数据最热,将最近写入的数据所有放在内存能够极大提高读取效率。Beringei很好的解决了这个问题,流式压缩意味着数据写入内存以后就进行压缩,这样会使得内存中能够缓存更多的时序数据,这样对于最近数据的查询会有很大的帮助。

 

Beringei的时序数据模型设计与InfluxDB基本一致,也是提出相似于SeriesKey的概念,将时间线挑了出来。但和InfluxDB有两个比较大的区别:

 

1. 文件组织形式不一样。Beringei的文件存储形式按照时间窗口组织,好比最近5分钟的数据所有写入同一个文件,这个文件分为不少block,每一个block中的全部时序数据共用一个SeriesKey。Beringei文件没有索引,InfluxDB有索引。

 

2. Beringei目前没有倒排索引机制,所以对于多维查询并不高效。

 

后续笔者也会针对Beringei的数据写入、流式压缩、文件格式等进行介绍。在笔者看来,若是将Beringei和InfluxDB有效结合起来,就可以将时序数据高效存储在内存,另外数据按照维度进行组织,能够很是高效的提升数据在文件的存储效率以及查询效率,最后结合InfluxDB的倒排索引功能能够有效提升多维查询能力。

 

本文是时序数据库技术体系的第一篇文章,笔者主要结合OpenTSDB、Druid、InfluxDB以及Beringei这四种时序数据库分别对时序数据这种数据形式的存储模型进行了介绍。每种数据库都有本身的一套存储方式,而每种存储方式都有各自的一些优点以及缺陷,正是这些优劣式直接决定了相应时序数据库的压缩性能、读写性能。

 

原文:时序数据库技术体系(一):时序数据存储模型设计

相关阅读:时序数据库技术体系(二) : 初识InfluxDB

更多网易技术、产品、运营经验分享敬请关注网易社区知乎机构号:网易云 - 知乎

相关文章
相关标签/搜索