Opentsdb 2.4版本新增了Rollup和pre-aggregator功能,在内外网搜索相关的文章除了官方文档及其翻译外资料甚少,而官方文档也非常粗略,使用中着实遇到了不少困难,现在记录一下。
TSDB的设计是存储全部频率的原始数据,这样在很广的时间范围内进行聚合查询通常会花费大量的时间才能完成,甚至会因为内存溢出异常而杀死TSDB。如一个opentsdb表记录4个节点每500ms的温度,当需要查看一个月的温度曲线时,query就会非常慢。
假设待存数据有如下metric和tag:
Series ID |
Metric |
Tag |
Tag 2 |
ts1 |
Signal |
node=A1 |
Name=temperature |
ts2 |
Signal |
node=B1 |
Name=temperature |
ts3 |
Signal |
node=B2 |
Name=temperature |
ts4 |
Signal |
node=A2 |
Name=temperature |
每500ms发送一包数据,假设某小时内温度数据如下
Series ID |
12:00:00 |
+0.5 |
+1.0 |
+1.5 |
+2.0 |
+2.5 |
+3.0 |
…… |
+3599.5 |
ts1 |
13 |
14 |
15 |
14 |
14 |
13 |
…… |
15 |
|
ts2 |
14 |
14 |
13 |
14 |
14 |
14 |
…… |
16 |
|
ts3 |
15 |
15 |
15 |
14 |
14 |
13 |
…… |
13 |
|
ts4 |
15 |
15 |
14 |
14 |
13 |
15 |
15 |
…… |
14 |
若每天运行15小时,当我们需要显示一个月运行时间内,4个节点内温度的曲线图时,后台系统需要向数据库请求得到30*15*7200*4=12960000个点的数据,并交由前端进行曲线绘制。这会在数据库请求时消耗大量时间,在数据发送给前端时占用大量带宽,也会给浏览器带来较大渲染压力。即使使用downsample,也需要数据库对12960000个数据进行聚合运算。而当我们需要显示一个月运行时间内,4个节点最高温度的曲线时,数据库需要取出12960000个数据对node=A1/B1/B2/A2的4个tag进行max聚合运算,同样需要消耗大量时间和性能。
当经常需要对opentsdb一个数据的大时间跨度的query和聚合运算时,我们可以对数据进行预汇总和预聚合,记录聚合结果,提高查询速度,降低运算压力。OpenTSDB 2.4版本提供了新的API,Rollup和Pre-Aggregates,用来存储原始数据的汇总和聚合结果以备快速查询。
rollup是将原始数据在每个整点开始的时候起,每interval间隔时间存入一次指定数据(如last/max/min/count等)。
pre-aggregates是对原始数据或Rollup数据指定的一组或多组tag进行聚合运算,再将运算结果预存入opentsdb。
具体来说就是对于每500ms一次的温度信号原始数据,如果在写入原始数据时,每interval间隔存入一次max/min/last等汇总数据和count,在查询时opentsdb将直接返回汇总数据,可提高查询性能。
例如上面温度的数据,可以令interval=1m,写入的汇总数据如下表:
Series ID |
12:00:00 |
+1m |
+2m |
+3m |
+4m |
+5m |
+6m |
…… |
+59m |
Ts1count |
59 |
60 |
57 |
59 |
60 |
58 |
59 |
…… |
60 |
Ts1sum |
727 |
741 |
735 |
743 |
746 |
728 |
734 |
…… |
746 |
…… |
…… |
…… |
…… |
…… |
…… |
…… |
…… |
…… |
…… |
opentsdb不提供生成和存入汇总和预聚合数据的方法,实现方案初步有3种:
延迟一段时间后从opentsdb读取数据,计算汇总和聚合,再写入opentsdb。
待写入数据写入队列,经过interval时间后计算队列里数据的sum,count或对某些tag的聚合等,再将原始数据和汇总/预聚合数据一起写入opentsdb。
使用流处理框架(Storm,Flink,Spark等)来处理消息路由和内存中的存储。
本次测试仅测试了Rollup功能,配置与测试步骤如下
{
"aggregationIds": {
"sum": 0,
"count": 1,
"min": 2,
"max": 3,
"last": 4
},
"intervals": [
{
"table": "tsdb-test",
"preAggregationTable": "tsdb-test-pre",
"defaultInterval": true,
"interval": "1s",
"rowSpan": "1h"
},
{
"table": "tsdb-test-rollup-1m",
"preAggregationTable": "tsdb-test-rollup-1m-pre",
"interval": "1m",
"rowSpan": "1d"
},
{
"table": "tsdb-test-rollup-1h",
"preAggregationTable": "tsdb-test-rollup-1h-pre",
"interval": "1h",
"rowSpan": "1y"
}
]
}
注意:①rowSpan不能超过interval的2^14倍约15k倍,比如1小时对1秒,1天对1分钟是合适的,1天(86k秒)对1秒将报错。
②上面的配置含义为,1秒为默认interval,请求若干秒的数据降采样(如40s-avg:和80s-sum:等)数据将使用原始数据进行计算。当降采样的interval为1分钟和1小时的倍数(如120s-sum, 24h-avg)时数据采用Rollup存入的数据。
③推荐1m rollup数据写入采取方法2,制作队列缓存要写入的原始数据,满1分钟后写入这1分钟的数据和汇总数据。
1h rollup数据写入采取方法1,设置触发器每小时读取上一小时所有的1m rollup数据进行rollup,将生成的1h rollup数据写入rollup-1h表中,供显示数天数十天信号曲线查询使用。
测试时写入原始数据和1m,1h的汇总数据,原始数据value=10,向/ai/rollup发送的假数据内容如下:
[
{
"metric": "OpentsdbTest",
"aggregator": "sum",
"interval": "1m",
"value": 3600,
"timestamp": 1583721527,
"tags": {
"node": "A1",
"signal": "temperature"
}
},
{
"metric": "OpentsdbTest",
"aggregator": "count",
"interval": "1m",
"value": 120,
"timestamp": 1583721527,
"tags": {
"node": "A1",
"signal": "temperature"
}
},
{
"metric": "OpentsdbTest",
"aggregator": "sum",
"interval": "1h",
"value": 720000,
"timestamp": 1583721527,
"tags": {
"node": "A1",
"signal": "temperature"
}
},
{
"metric": "OpentsdbTest",
"aggregator": "count",
"interval": "1h",
"value": 70000,
"timestamp": 1583721527,
"tags": {
"node": "A1",
"signal": "temperature"
}
},
{…node A2的1m和1h数据…},
{……}
]
注意:Rollup数据的timestamp只能用10位的秒时间戳,使用13位的毫秒时间戳会报错(虽然官方文档显示可以用毫秒时间戳)
1m rollup数据和1h rollup数据是用于测试的假数据,写入的数据特意不匹配用来query不同时间降采样观察读取的是哪个数据。写入一段时间后读取数据库中的数据,除去写入不足1分钟的部分,结果大致如下:
query |
结果 |
含义 |
sum: |
10 |
原始数据 |
sum:1m-sum: |
3600 |
1m rollup数据 |
sum:60s-sum: |
3600 |
1m rollup数据 |
sum:70s-sum: |
1400 |
原始数据聚合 |
sum:2m-sum |
7200 |
1m rollup数据聚合 |
sum:1h-sum |
70000 |
1h rollup数据 |