时间序列数据的存储和计算 - 开源时序数据库解析(一)

开源时序数据库

如图是17年6月在db-engines上时序数据库的排名,在本篇以及后续的几篇文章中我会挑选开源的、分布式的时序数据库做详细的解析。前十的排名中,RRD是一个老牌的单机存储引擎,Graphite底层是Whisper,可以认为是一个优化的更强大的RRD数据库。kdb+、eXtremeDB和Axibase都未开源,不做解析。

我会选择HBase系代表OpenTSDB,Cassandra系代表KairosDB、BlueFlood和Herioc,以及目前排名第一完全自研的InfluxDB做一个详细的解析。我对OpenTSDB比较熟悉,研究过它的源码,所以对OpenTSDB会描述的格外详细,而对其他时序数据库了解的没那么深入,如果有理解错的地方,欢迎指正。

OpenTSDB

OpenTSDB是一个分布式、可伸缩的时序数据库,支持高达每秒百万级的写入能力,支持毫秒级精度的数据存储,不需要降精度也可以永久保存数据。其优越的写性能和存储能力,得益于其底层依赖的HBase,HBase采用LSM树结构存储引擎加上分布式的架构,提供了优越的写入能力,底层依赖的完全水平扩展的HDFS提供了优越的存储能力。OpenTSDB对HBase深度依赖,并且根据HBase底层存储结构的特性,做了很多巧妙的优化。关于存储的优化,我在这篇文章中有详细的解析。在最新的版本中,还扩展了对BigTable和Cassandra的支持。

架构

如图是OpenTSDB的架构,核心组成部分就是TSD和HBase。TSD是一组无状态的节点,可以任意的扩展,除了依赖HBase外没有其他的依赖。TSD对外暴露HTTP和Telnet的接口,支持数据的写入和查询。TSD本身的部署和运维是很简单的,得益于它无状态的设计,不过HBase的运维就没那么简单了,这也是扩展支持BigTable和Cassandra的原因之一吧。

数据模型

OpenTSDB采用按指标建模的方式,一个数据点会包含以下组成部分:

metric:时序数据指标的名称,例如sys.cpu.user,stock.quote等。

timestamp:秒级或毫秒级的Unix时间戳,代表该时间点的具体时间。

tags:一个或多个标签,也就是描述主体的不同的维度。Tag由TagKey和TagValue组成,TagKey就是维度,TagValue就是该维度的值。

value:该指标的值,目前只支持数值类型的值。

存储模型

OpenTSDB底层存储的优化思想,简单总结就是以下这几个关键的优化思路:

对数据的优化:为Metric、TagKey和TagValue分配UniqueID,建立原始值与UniqueID的索引,数据表存储Metric、TagKey和TagValue对应的UniqueID而不是原始值。

对KeyValue数的优化:如果对HBase底层存储模型十分了解的话,就知道行中的每一列在存储时对应一个KeyValue,减少行数和列数,能极大的节省存储空间以及提升查询效率。

对查询的优化:利用HBase的Server Side Filter来优化多维查询,利用Pre-aggregation和Rollup来优化GroupBy和降精度查询。

UIDTable

接下来看一下OpenTSDB在HBase上的几个关键的表结构的设计,首先是tsdb-uid表,结构如下:

Metric、TagKey和TagValue都会被分配一个相同的固定长度的UniqueID,默认是三个字节。tsdb-uid表使用两个ColumnFamily,存储了Metric、TagKey和TagValue与UniqueID的映射和反向映射,总共是6个Map的数据。

从图中的例子可以解读出:

TagKey为'host',对应的UniqueID为'001'

TagValue为'static',对应的UniqueId为'001'

Metric为'proc.loadavg.1m',对应的UniqueID为'052'

为每一个Metric、TagKey和TagValue都分配UniqueID的好处,一是大大降低了存储空间和传输数据量,每个值都只需要3个字节就可以表示,这个压缩率是很客观的;二是采用固定长度的字节,可以很方便的从row key中解析出所需要的值,并且能够大大减少Java堆内的内存占用(bytes相比String能节省很多的内存占用),降低GC的压力。

不过采用固定字节的UID编码后,对于UID的个数是有上限要求的,3个字节最多只允许有16777216个不同的值,不过在大部分场景下都是够用的。当然这个长度是可以调整的,不过不支持动态更改。

DataTable

第二张关键的表是数据表,结构如下:

该表中,同一个小时内的数据会存储在同一行,行中的每一列代表一个数据点。如果是秒级精度,那一行最多会有3600个点,如果是毫秒级精度,那一行最多会有3600000个点。

这张表设计的精妙之处在于row key和qualifier(列名)的设计,以及对整行数据的compaction策略。row key格式为:

...

其中metric、tagk和tagv都是用uid来表示,由于uid固定字节长度的特性,所以在解析row key的时候,可以很方便的通过字节偏移来提取对应的值。Qualifier的取值为数据点的时间戳在这个小时的时间偏差,例如如果你是秒级精度数据,第30秒的数据对应的时间偏差就是30,所以列名取值就是30。列名采用时间偏差值的好处,主要在于能大大节省存储空间,秒级精度的数据只要占用2个字节,毫秒精度的数据只要占用4个字节,而若存储完整时间戳则要6个字节。整行数据写入后,OpenTSDB还会采取compaction的策略,将一行内的所有列合并成一列,这样做的主要目的是减少KeyValue数目。

查询优化

HBase仅提供简单的查询操作,包括单行查询和范围查询。单行查询必须提供完整的RowKey,范围查询必须提供RowKey的范围,扫描获得该范围下的所有数据。通常来说,单行查询的速度是很快的,而范围查询则是取决于扫描范围的大小,扫描个几千几万行问题不大,但是若扫描个十万上百万行,那读取的延迟就会高很多。

OpenTSDB提供丰富的查询功能,支持任意TagKey上的过滤,支持GroupBy以及降精度。TagKey的过滤属于查询的一部分,GroupBy和降精度属于对查询后的结果的计算部分。在查询条件中,主要的参数会包括:metric名称、tag key过滤条件以及时间范围。上面一章中指出,数据表的rowkey的格式为:...,从查询的参数上可以看到,metric名称和时间范围确定的话,我们至少能确定row key的一个扫描范围。但是这个扫描范围,会把包含相同metric名称和时间范围内的所有的tag key的组合全部查询出来,如果你的tag key的组合有很多,那你的扫描范围是不可控的,可能会很大,这样查询的效率基本是不能接受的。

我们具体看一下OpenTSDB对查询的优化措施:

Server side filter

HBase提供了丰富和可扩展的filter,filter的工作原理是在server端扫描得到数据后,先经过filter的过滤后再将结果返回给客户端。Server side filter的优化策略无法减少扫描的数据量,但是可以大大减少传输的数据量。OpenTSDB会将某些条件的tag key filter转换为底层HBase的server side filter,不过该优化带来的效果有限,因为影响查询最关键的因素还是底层范围扫描的效率而不是传输的效率。

减少范围查询内扫描的数据量

要想真正提高查询效率,还是得从根本上减少范围扫描的数据量。注意这里不是减小查询的范围,而是减少该范围内扫描的数据量。这里用到了HBase一个很关键的filter,即FuzzyRowFilter,FuzzyRowFilter能够根据指定的条件,在执行范围扫描时,动态的跳过一定数据量。但不是所有OpenTSDB提供的查询条件都能够应用该优化,需要符合一定的条件,具体要符合哪些条件就不在这里详述了,有兴趣的可以去了解下FuzzyRowFilter的原理。

范围查询优化成单行查询

这个优化相比上一条,更加的极端。优化思路非常好理解,如果我能够知道要查询的所有数据对应的row key,那就不需要范围扫描了,而是单行查询就行了。这里也不是所有OpenTSDB提供的查询条件都能够应用该优化,同样需要符合一定的条件。单行查询要求给定确定的row key,而数据表中row key的组成部分包括metric名称、timestamp以及tags,metric名称和timestamp是能够确定的,如果tags也能够确定,那我们就能拼出完整的row key。所以很简单,如果要能够应用此优化,你必须提供所有tag key对应的tag value才行。

  以上就是OpenTSDB对HBase查询的一些优化措施,但是除了查询,对查询后的数据还需要进行GroupBy和降精度。GroupBy和降精度的计算开销也是非常可观的,取决于查询后的结果的数量级。对GroupBy和降精度的计算的优化,几乎所有的时序数据库都采用了同样的优化措施,那就是pre-aggregation和auto-rollup。思路就是预先进行计算,而不是查询后计算。不过OpenTSDB在已发布的最新版本中,还未支持pre-aggregation和rollup。而在开发中的2.4版本中,也只提供了半吊子的方案,它只提供了一个新的接口支持将pre-aggregation和rollup的结果进行写入,但是对数据的pre-aggregation和rollup的计算还需要用户自己在外层实现。

总结

  OpenTSDB的优势在于数据的写入和存储能力,得益于底层依赖的HBase所提供的能力。劣势在于数据查询和分析的能力上的不足,虽然在查询上已经做了很多的优化,但是不是所有的查询场景都能适用。可以说,OpenTSDB在TagValue过滤查询优化,是这次要对比的几个时序数据库中,优化的最差的。在GroupBy和Downsampling的查询上,也未提供Pre-aggregation和Auto-rollup的支持。不过在功能丰富程度上,OpenTSDB的API是支持最丰富的,这也让OpenTSDB的API成为了一个标杆

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容