openTSDB详解之Rollup and pre-aggregates【待完善】
尽管TSDB被设计为:只要仍有存储空间的话,就会存储源头的,全部的频率数据,但是很广的时间范围中且有许多标签集合中的查询,仍然是很痛苦的。这类查询经常花费大量的时间才能完成,或者差点儿说,将会杀死TSDs因为没有内存可用的异常。对于版本2.4一系列新的API允许存储并且查询低频数据,为了更加迅速地回答这类查询。这页内容,将会给你一个概览:什么是Rollup and pre-aggregates?他们在TSDB中是怎么工作的?以及怎么充分利用他们?如果要了解详细的细节,请看api的部分。
Note
opentsdb自己并不计算和存储rollup 或者是 pre-aggregated 数据。这里有多种方式去计算结果,但是在不同的数据规模和精度要求中,它们各有利弊。详见…部分去讨论如何生成这个数据。
Example Data
为了帮助描述低频数据,然我们先看看一些全频(同时也是作为原生数据)例子。下面第一张表定义了时间序列,使用一个简短的标志符。
Series
ID Metric Tag 1 Tag 2 Tag 3
ts1 system.if.bytes.out host=web01 colo=lga interface=eth0
ts2 system.if.bytes.out host=web02 colo=lga interface=eth0
ts3 system.if.bytes.out host=web03 colo=sjc interface=eth0
ts4 system.if.bytes.out host=web04 colo=sjc interface=eth0
注意,他们都有相同的metric和interface tag,但是不同的host和colo tag。
接下来一些数据以每15min写入;
Series
ID 12:00 12:15 12:30 12:45 13:00 13:15 13:30 13:45
ts1 1 4 -3 8 2 -4 5 2
ts2 7 2 8 -9 4 1 1
ts3 9 3 -2 -1 6 3 8 2
ts4 2 5 2 8 5 -4 7
注意一些数据点丢失了。对于这些数据集,让我们先看rollups(原文:With those data sets,lets look at rollups first)。
Rollups
一个rollup是被定义了,在openTSDB中,作为单个时间序列聚合,在一段时间内。所以它也可以被称为“time-based aggregation”。Rollups帮助解决问题,寻找广泛的时间范围。例如:如果你写数据点每60秒,并且查询一年的数据,一个时间序列将会返回超过525k个时间点。画图,这些数据点将会看起来十分的混乱。相反,你可能想看最低的频率数据,假设一小时的数据,你仅仅只有8k数据点去绘制。然后,你能够标识异常现象,并且深思更好的数据频率。
如果你已经使用了openTSDB去查询数据,你可能熟悉downsamples,这是一个聚合每个时间序列成一个小的,或者是更少的频率的值。一个rollup是必要的结果 downsampler 存储在系统中,并且调用任意。每个Rollup(或者是downsampler)要求两个信息:
- Interval:新值中有多少时间是被混合了?例如,1h对于每小时的数据,1d是对于每天的数据。
- Aggregation Function:将底层值变为新值的具体应用算法是什么?例如,sum:是取所有的值;max:则是存储最大值。
warning
当存储rollups时,最好避免函数:average,median,或者是deviation。当执行更深层的downsampling或者分组aggregations时,这类值将会变得毫无意义。相反,存储sum或者是counter将会好很多,至少,average函数将会被计算在查询的时候。要了解更多的信息,查看下列的部分。
rolled-up的时间戳将会成为Rollup 间隔的顶部。(原文:The timestamp of a rolled-up data point should snap to the top of the rollup interval.)例如:如果Rollup interval是1h,那么它包含数据的一小时并且应该轻拍到小时的顶部。(因为所有的时间戳是被写入以Unix 的格式,以UTC时区定义,这将成为UTC时间的开始。)
Rollup Pre-Aggregates
尽管pre-aggregates的确在很多metrics时有所帮助,但用户可能仍然想访问广泛时间范围,但是却陷入缓慢的查询。感谢你能roll up 一个pre-aggregate用相同的方式作为初始数据【幸运的是,你可以用与原始数据相同的方式来建立预聚合(原文:Thankfully you can roll up a pre-aggregate in the same way as raw data)】。仅仅生成pre-aggregate,然后使用上述的信息roll it up。
Generating Rollups and Pre-aggregates
目前,TSDs没有为你生成rollup或者是pre-aggregated data。主要的原因是:openTSDB原本是处理大量的时间序列数据所以单独的TSDs集中精力在尽快的存储数据。
Problems
因为TSDs(本质上)的无状态性,它们极有没有全部可用的数据去做pre-aggregates。例如:我们的例子:ts1数据可能被写入TSD_A然而ts2被写入到TSD_B中。没有谁可以执行一个合适的分组,如果没有读存储之外的数据。我们同样也不知道在什么时间,我们应该执行pre-aggregation。我们能够等待1分钟并且pre-aggregate 数据但是丢失在一分钟之后进入的所有数据。或者我们能够等一个小时,然后基于pre-aggregates的查询不会获得最近一个小时的数据。如果数据稍后进来,将会发生什么?
rolllups附加的,取决于用户怎样往TSDs中写入数据,对于ts1,我们可能会收到12:15数据点在TSD_A,但是在12:30值在TSD_B,so neither has the data required for the full hour.时间窗口限制同样应用于rollups。
Solutions
使用rollups并且pre-aggregates要求一些分析,并且一个选择在不同的trade-offs中。因为一些openTSDB用户已经有方式去计算这种数据,我们简单地提供API去存储、查询。然而这里有一些建议对于如何使用。
Batch processing
被其它时间序列数据库常用的方式是读取数据库之外的数据,在一段延迟之后,计算pre-aggs以及rollups,然后写。这是最简单的方式去处理问题,并且在小规模数据时工作良好,然而仍然有一系列的问题:
- 随着数据的增长,生成rollups的查询也会增长到查询负载影响书写和用户查询性能的程度。(原文:As data grows,queries for generating the rollups grow as well to the point where the query load impacts write and user query performance.)openTSDB遇到同样的问题,当数据压缩被开启时在HBase中。
- 同样随着数据增长,更多的数据意味着批处理花费更长时间,必须被分享根据多个worker,这是协作处理或者问题寻找时的一个痛苦所在。
- 晚点的或者历史的数据可能不会被roll up 除非一些跟踪的方式为了取代trigger,一个新的批次在旧数据。
一些提高批处理的方法如下:
-从重复的系统中读取数据,例如:如果你开启Hbase 备份功能,你能够让用户查询主系统和从备份存储中读取聚合。
-从可选的存储中读取数据。一个明显的例子是:像HDFS一样镜像存储数据,并对该数据运行批量的job。
Queueing on TSDs
另一个选项:一些数据库在处理过程中使用的是排队所有的数据在内存中,并且写结果在一个配置的时间窗口之后。但是因为TSDs是无状态的并且通常用户在TSDs前放了一个负载均衡器,单个TSD可能不会得到rollup全部的图片或者是pre-agg去计算(因为我们上述提及)。为了让这个方法生效,上游收集器必须将计算所需的所有数据路由到特定的TSD。这不是一项艰巨的任务,但所面临的问题有:
- 对于每个TSD,有足够的本地RAM(内存)或者是磁盘空间去缓冲数据
- 如果一个TSD进程死掉,你要么为了聚合而松散数据,要么为了存储被引导。
- 无论何时聚合计算发生,所有的原数据的写吞吐量将会被影响。
- 你将仍然有最新的/历史的数据问题。
- 因为TSDB是基于jvm的,保持所有的数据在RAM中,然后运行GC将会效率低下。(在磁盘中进行缓冲比较好,但是你将遇到IO问题)
通常情况,对一个writer排队是很差的注意。要注意避免痛苦的发生。
Stream Processing
一个更好的方式去处理rollups并且pre-aggregates是去路由数据到一个流处理系统,在流处理器系统中,能够被处理在接近实时并且写入TSDs。这是和在TSDs中排对像似但是使用到了无数流处理器框架的其中之一(例如:Storm,Flik,Spark等)去处理消息路由并且存储在内存中。那么你简单地写一些编码去计算aggregates以及分割数据在一个窗口被传递之后。
这是一个解决办法被许多下一代监控处理诸如在Yahoo!.Yahoo正在工作去开源它们的流处理系统为了其他人——需要监控在大规模并且它可整洁地插入到TSDB中。
尽管流处理器工作良好,但是你仍然会遇到诸如以下的问题:
- 处理这些job,你需要足够的资源
- 一个死掉的worker要求重新从存储器中引导
- 最新的/历史的数据必须被处理
Share
如果你有计算聚合的有效代码,请分享给openTSDB小组。如果你的处理办法是开源的,我们可能有能力包含在openTSDB生态系统中。
Configuration
对于openTSDB2.4版本,the rollup 配置是被引用,通过opentsdb.conf中的键:tsd.rollups.config。这个键的值必须是一个没有换行符的引用转义的JSON字符串,或者最好是包含配置的JSON文件的路径。文件名必须以.json结尾比如rollup_config.json。
json配置文件应该看起来像是这个样子:
{
“aggregationIds”: {
“sum”: 0,
“count”: 1,
“min”: 2,
“max”: 3
},
“intervals”: [{
“table”: “tsdb”,
“preAggregationTable”: “tsdb-preagg”,
“interval”: “1m”,
“rowSpan”: “1h”,
“defaultInterval”: true
}, {
“table”: “tsdb-rollup-1h”,
“preAggregationTable”: “tsdb-rollup-preagg-1h”,
“interval”: “1h”,
“rowSpan”: “1d”
}]
}
两个*域包括:
- aggregationIds:为了压缩存储而实现的一个openTSDB聚合函数名字到数字标识符映射
- intervals 一个或多个interval 标识符的list。【这些标志符包涵表名以及interval 标识】
aggregationIds
聚合键映射被用于减少存储,通过预先考虑每种类型的rolled updata和数字的ID,相反不是评出完整的聚合函数。例如,如果我们在每列前加上前缀COUNT:那将有6字节对于每个值(或者是压缩的列),我们能够使用一个ID。
IDs必须是从0-127的整数。这意味着我们能够粗糙农户128个不同的rollups对于每个interval。仅仅每个数字值的一个ID可能被提供在映射中,并且每种类型的聚合函数被提供。如果一个函数名没有映射到聚合函数,由openTSDB支持,一个异常将会被抛出在启动时。同样,至少一个聚合被给出,在TSD启动时。
Warning
aggregation ID不能被改变,一旦你开始写数据。如果你想改变映射,那么不正确的数据将会被返回或者查询、写数据将会被遇到错误。你能总是添加函数在将来,但是绝不能改变映射。
intervals
每个interval对象定义表路由,为了rollup 以及 pre-aggregate数据,应该从哪里被写入以及查询。这里有两种interval的类型:
- default:由”defaultInterval:true”定义,这个是默认的,原数据openTSDB表。对于存在的装置来说,这将是tsdb表或者被定义在tsd.storage.hbase.data_table中的表。intervals 以及 spans均被忽略,默认到openTSDB中1小时,并且时间戳给出。每个TSD以及配置能够被默认。
- Rollup Interval:任何interval伴随”defaultInterval”:false或者是默认interval没有设置。这些是rollup表,值是被存储中哎interval边界中。
下列的字段应该被定义:
····
在存储中,rollups与raw data是相似的写入,因为每行有一个基础的时间戳,并且每个数据点是从基础时间偏移而来。每个偏移值是一个距基础时间的增量,并费事一个实际偏移。例如:如果一行存储1天+1小时的数据(原文:if a row stores 1 day of 1 hour data)总共将有24个偏移量。偏移量0将会映射在midnight,偏移量5将会映射到AM 6点。因为rollup 偏移量是用14位编码,如果太多的intervals被存储在一行中,为了适应14位,一个错误将会被抛出,当TSD启动时。
Warnings
在往TSD写数据之后,不要改变interval的宽度,或者是row spans为了rollup intervals。这将会导致垃圾数据,并且造成查询失败。