opentsdb 写入和查询详细讲解

引言

前面博主写了一篇文章去介绍opentsdb的http接口的使用方法,但是某一些接口的使用还是比较复杂,这篇文章会通过example来详细讲述opentsdb的一些特性。

本文的举的例子有这些:

  1. 基本的写入和查询
  2. 数据的注释和说明
  3. 子查询
  4. 查询中的filters使用
  5. 查询数据的rate(增长率)
  6. 直方图中百分位数(percentiles)的查询
  7. Downsampling(下采样)
  8. query/exp 的使用(查询中使用表达式)
  9. trees详解

一、基本的写入和查询

这个功能是最基本,也是最常用的。
写数据:写入数据post接口为 /api/put?details,details表示会将写入的详细结果返回回来:

#请求体
[
    {
        "metric": "sys.cpu.nice",
        "timestamp": 1346846402,
        "value": 18,
        "tags": {
           "host": "web01",
           "dc": "lga"
        }
    }
]
#写入成功返回的内容
{
    "success": 1,
    "failed": 0,
    "errors": []
}

查数据:写入成功之后,当然可以去查询。查询post接口为 /api/query:

#请求体
{
    "start": 1346846402,
    "end": 1346846403,
    #返回数据对应的tsUID
    "showTSUIDs":"true",  
    "queries": [
        {
            "aggregator": "avg",
            "metric": "sys.cpu.nice",
            "tags": {
                 "host": "web01",
                 "dc": "lga"
             }
        }
    ]
}
#返回数据
[
    {
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web01",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000001000002000002"  #数据对应的tsUID
        ],
        "dps": {
            "1346846402": 18
        }
    }
]

这里需要对tsUID进行说明一下,opentsdb是由metric+tags来区分数据的,当metric和tags相同时,其tsUID就会相同,代表着同一系列的数据。那么,假如我们想对这一系列数据进行标注和说明呢?见下一个example。

二、数据的注释和说明

数据的注释和说明是用到了 /api/annotation 接口,post方式是写入annotation数据,get是查询annotation数据。

#post接口的请求body
{
  "startTime":"1346846402",
  #和返回前面一个example返回tsUID相同,这样时间序列数据就和annotation数据关联了起来,可作为时间序列数据的注释和说明
  "tsuid":"000001000001000001000002000002",
  "description": "Testing Annotations",
  "notes": "These would be details about the event, the description is just a summary",
  "custom": {
      "owner": "jdoe",
      "dept": "ops"
  }
}

当写入成功时间,再次运行查询example1中的 /api/query 请求,即可得到:

[
    {
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web01",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000001000002000002"
        ],
        "annotations": [
            {
                "tsuid": "000001000001000001000002000002",
                "description": "Testing Annotations",
                "notes": "These would be details about the event, the description is just a summary",
                "custom": {
                    "owner": "jdoe",
                    "dept": "ops"
                },
                "startTime": 1346846402,
                "endTime": 0
            }
        ],
        "dps": {
            "1346846402": 18
        }
    }
]

可见,此次在返回数据的清楚上,把相关联的注释(annotation数据)也一起返回回来,注释一般可以用来解释和说明数据。

三、子查询

/api/query 接口中,body中有一个参数是queries,它表示可以含有多个子查询,所谓子查询就是只要数据满足其中的一个子查询,数据就会返回回来。注意每次查询至少需要一个子查询。

在example1中写入一条数据的前提下,这里再向tsdb中写入一条数据:

[
    {
        "metric": "sys.cpu.nice",
        "timestamp": 1346846402,
        "value": 9,
        "tags": {
           "host": "web02",
           "dc": "lga"
        }
    }
]
# 通过 /api/query 接口我们可以查得该条数据的tsUID为000001000001000003000002000002

下面查询body就表示有两个子查询:

//请求体
{
    "start": 1346846401,
    "end": 1346846403,
    "showTSUIDs":"true",
    "queries": [
        {   //第一个子查询,查询的是example1中写入的数据
            "aggregator": "avg",
            "metric": "sys.cpu.nice",
            "tags": {
                 "host": "web01",
                 "dc": "lga"
             }
        },
        {   //第二个子查询,查询的是刚刚写入的数据
            "aggregator": "avg",
            "tsuids":["000001000001000003000002000002"]
        }
    ]
}

//返回结果
[
    {    //第一个子查询对应的数据
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web01",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000001000002000002"
        ],
        "annotations": [
            {
                "tsuid": "000001000001000001000002000002",
                "description": "Testing Annotations",
                "notes": "These would be details about the event, the description is just a summary",
                "custom": {
                    "owner": "jdoe",
                    "dept": "ops"
                },
                "startTime": 1346846402,
                "endTime": 0
            }
        ],
        "dps": {
            "1346846402": 18
        }
    },
    {     //第二个子查询对应的数据
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web02",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000003000002000002"
        ],
        "dps": {
            "1346846402": 9
        }
    }
]

在平常使用过程中我们可以使用单个或者多个子查询,还有需要注意对于每个子查询而言,主要有两种类型:

  1. metric查询方式:子查询指定metric和tags(optional)进行查询,本次查询中的第一个子查询就是采用这种方式。
  2. TSUID查询方式:需要给出一个或者多个tsuid,对应本次查询中的第二个子查询。

四、查询中的filters使用

从opentsdb2.2版本便支持filter,它其实是用于过滤tags的,可以作为tags查询的替代者,并且比tags更加灵活。请求body如下:

{
    "start": 1346846401,
    "end": 1346846403,
    "showTSUIDs":"true",
    "queries": [  
        {
            "aggregator": "avg",
            "metric": "sys.cpu.nice",
            "filters": [
                {  
                   "type":"literal_or",  
                   "tagk":"host",
                   "filter":"web01|web02",
                   "groupBy":true
                }
            ]
        }
    ]
}
参数 意义
type 过滤器的类型,可以访问 /api/config/filters 接口查看支持的所有类型,这里 literal_or 表示value是一个枚举
tagk 指定过滤的key
filter 和相type对应,这里表示对web01和web02都进行匹配
groupBy 是否对匹配到的数据进行分组

这里使用literal_or,filter里面的多个tagV以竖线相隔,这个过滤器的意思是对tagK为host进行匹配,并且value为web01和web02都数据都会匹配成功。
返回结果:

[
    {
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web01",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000001000002000002"
        ],
        "annotations": [
            {
                "tsuid": "000001000001000001000002000002",
                "description": "Testing Annotations",
                "notes": "These would be details about the event, the description is just a summary",
                "custom": {
                    "owner": "jdoe",
                    "dept": "ops"
                },
                "startTime": 1346846402,
                "endTime": 0
            }
        ],
        "dps": {
            "1346846402": 18
        }
    },
    {
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web02",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000003000002000002"
        ],
        "dps": {
            "1346846402": 9
        }
    }
]

可见本次filter查询用一个子查询的结果和example3中用了两个子查询的效果是一样的。

五、查询数据的rate(增长率)

在某些情况下,我们查询的可能并不是数据的本身,而是它的增长率。恰巧opentsdb有帮我们提供这个功能:子查询中的rate参数。
首先我们先写入3条数据,时间分别间隔两秒,数据分别为0、64000和1000。

[
    {
        "metric": "sys.cpu.nice",
        "timestamp": 1346846410,
        "value": 0,
        "tags": {
           "host": "web03",
           "dc": "lga"
        }
    },
    {
        "metric": "sys.cpu.nice",
        "timestamp": 1346846412,
        "value": 64000,
        "tags": {
           "host": "web03",
           "dc": "lga"
        }
    },
    {
        "metric": "sys.cpu.nice",
        "timestamp": 1346846414,
        "value": 1000,
        "tags": {
           "host": "web03",
           "dc": "lga"
        }
    }
]

查询增长率的请求body如下:

{
    "start": 1346846409,
    "end": 1346846414,
    "showTSUIDs":"true",
    "queries": [  
        {
            "aggregator": "avg",
            "metric": "sys.cpu.nice",
            "rate":true,  # 查询增长率
            "rateOptions":{
                "counter":false  
            },
            "tags": {
                 "host": "web03",
                 "dc": "lga"
             }
        }
    ]
}

# 响应结果
[
    {
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web03",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000007000002000002"
        ],
        "dps": {
            "1346846412": 32000,
            "1346846414": -31500
        }
    }
]

3200=(6400-0)/2,-31500=(1000-6400)/2,可见增长率是以秒为单位。

六、直方图中百分位数(percentiles)的查询

opentsdb在2.4版本对直方图(histogram进行了支持),本个example中首先写入直方图数据,然后根据数据对百分位数(percentile)进行查询。
写入数据的body如下:buckets是直方图数据,意思为0到1.75区间的数值为12,1.75到3.5区间的数值为16.

{
    "metric": "sys.cpu.nice",
    "timestamp": 1356998400,
    "overflow": 1,
    "underflow": 0,
    "buckets": { 
        "0,1.75": 12,
        "1.75,3.5": 16
    },
    "tags": {
        "host": "web01",
        "dc": "lga"
    }
}

关于百分位的定义可以自行查资料进行详细认识,本次查询中percentiles列表里面就是需要查询的百分位,需要注意的是列表里面的数字的取值区间是[0,100],并且可以不按照顺序排列。查询body如下:

{
    "start": 1356998400,
    "end": 1356998401,
    "showTSUIDs":"true",
    "queries": [  
        {
            "aggregator": "sum",
            "percentiles":  [100,99,43,42,1],
            "metric": "sys.cpu.nice",
            "tags": {
                 "host": "web01",
                 "dc": "lga"
             }
        }
    ]
}

请求的结果如下:

[
    {
        "metric": "sys.cpu.nice_pct_1.0",
        "tags": {
            "host": "web01",
            "dc": "lga"
        },
        "aggregateTags": [],
        "tsuids": [
            "000001000001000001000002000002"
        ],
        "dps": {
            "1356998400": 0.875
        }
    },
    {
        "metric": "sys.cpu.nice_pct_42.0",
         ···
        "dps": {
            "1356998400": 0.875
        }
    },
    {
        "metric": "sys.cpu.nice_pct_43.0",
         ···
        "dps": {
            "1356998400": 2.625
        }
    },
    {
        "metric": "sys.cpu.nice_pct_99.0",
        ···
        "dps": {
            "1356998400": 2.625
        }
    },
    {
        "metric": "sys.cpu.nice_pct_100.0",
        ···
        "dps": {
            "1356998400": 2.625
        }
    }
]

返回内容如上:其中相同部分已经省略,返回的metric由 原始metric_pct_number 组成,下面讲述它们的计算方式:
第一个区间的数值为12,第二个区间的数值为16,12/(12+16)=0.428。

  • 我们看到1和42的百分位的取值都是0.875,0.875=1.75/2,取的第一个区间的中点坐标,可以得到在0.428之前的百分位的数值都为0.875。
  • 43、99、100百分位对应的数值都为2.625,2.625=1.75+(3.5-1.75)/2,2.625的物理意义就是第二个区间中点的横坐标,因此43到100之间的百分位取值都为2.525。

七、Downsampling(下采样)

下采样即让浓密数据变稀疏的过程,首先写入10条数据,数值分别为0到9,相邻数据的时间间隔为1s:

[
    {
        "timestamp": 1562068000,
        "value": 0,
        "metric": "sys.cpu.nice",
        "tags": {
           "host": "web01",
           "dc": "lga"
        }
    },
    ······
    {
        "metric": "sys.cpu.nice",
        "timestamp": 1562068009,
        "value": 9,
        "tags": {
           "host": "web01",
           "dc": "lga"
        }
    }
]

下采样查询如下,downsample字段是一个字符串,该字段由 interval-aggregate-fill policy 组成,分别表示时间间隔、聚合方法、缺少的值补齐的方法。本次查询下采样间隔为2s,聚合方法是取聚合区间的最小值,并且缺少的值用0补齐:

{
    "start": 1562068000,
    "end": 1562068009,
    "queries": [  
        {
            "aggregator": "avg",
            "metric": "sys.cpu.nice",
            "downsample":"2s-min-zero",
            "tags": {
                 "host": "web01",
                 "dc": "lga"
             }
        }
    ]
}

返回结果如下,可见原本每秒一个数据在结果中是每两秒返回一个数据,并且在每个间隔中,都是取的最小值。

[
    {
        "metric": "sys.cpu.nice",
        "tags": {
            "host": "web01",
            "dc": "lga"
        },
        "aggregateTags": [],
        "dps": {
            "1562068000": 0,
            "1562068002": 2,
            "1562068004": 4,
            "1562068006": 6,
            "1562068008": 8
        }
    }
]

八、query/exp 的使用(查询中使用表达式)

这个接口允许使用表达式进行查询,可以对查询的多个结果进行操作。
在example7写入数据的基础上,再写入如下数据,相比example7的数据而言仅仅是metric发生了变化:

[
    {
        "timestamp": 1562068000,
        "value": 0,
        "metric": "sys.cpu.nice1",
        "tags": {
           "host": "web01",
           "dc": "lga"
        }
    },
    ······
    {
        "metric": "sys.cpu.nice1",
        "timestamp": 1562068009,
        "value": 9,
        "tags": {
           "host": "web01",
           "dc": "lga"
        }
    }
]

紧接着使用表达式进行查询,查询body如下,

  • time 定义了查询的时间区间和聚合方式
  • filters 定义了一个过滤器f1
  • metric 中指定了对sys.cpu.nice和sys.cpu.nice1两个metric进行查询,并且两个metric都使用同一个filter:f1
  • expressions 中是语法表达式,e就等于结果a加上结果b,e2就等于e乘以2
  • outputs 指定需要输出的表达式计算结果
{
   "time": {"start": "1562068000","end":"1562068009","aggregator":"sum"},
   "filters": [{ "tags": [{"type": "wildcard","tagk": "host","filter": "web*","groupBy": true}],
           "id": "f1"}],
   "metrics": [{"id": "a","metric": "sys.cpu.nice","filter": "f1","fillPolicy":{"policy":"nan"}},
               {"id": "b", "metric": "sys.cpu.nice1","filter": "f1","fillPolicy":{"policy":"nan"}}],
   "expressions": [{"id": "e","expr": "a + b"},
                   {"id":"e2","expr": "e * 2"}],
    "outputs":[{"id":"e", "alias":"e"},{"id":"e2", "alias":"e2"}]
 }

查询结果如下,query是里面是关于查询请求body的信息,为了节约空间这里省略。可以得知表达式计算是对同一个时间点进行计算的。

  • outputs中的e,时间点1562068001000对应的值为2,sys.cpu.nice和sys.cpu.nice1在1562068001000对应的数值都为1,便可和表达式中 e=a+b 对应起来。
  • e2中时间点1562068001000对应的值为4,便可和表达式中 e2=ex2 对应起来。
{
    "outputs": [
        {
            "id": "e",
            "alias": "e",
            "dps": [ [1562068000000,0],[1562068001000,2],[1562068002000,4],[ 1562068003000, 6],[1562068004000,8],[1562068005000,10],[1562068006000,12],[1562068007000,14],[1562068008000,16],[1562068009000,18] ],
            "dpsMeta": { "firstTimestamp": 1562068000000,"lastTimestamp": 1562068009000, "setCount": 10,"series": 1
            },
            "meta": [{"index":0,"metrics":["timestamp"]},{"index":1,"metrics":["sys.cpu.nice","sys.cpu.nice1"],"commonTags":{"host":"web01","dc":"lga"},"aggregatedTags":[]}]
        },
        {
            "id": "e2",
            "alias": "e2",
            "dps": [[1562068000000,0],[1562068001000,4],[1562068002000,8],[1562068003000,12],[1562068004000,16],[1562068005000,20],[1562068006000,24],[1562068007000,28],[1562068008000,32],[1562068009000,36]],
            "dpsMeta": { "firstTimestamp": 1562068000000,"lastTimestamp": 1562068009000,"setCount": 10,"series": 1},
            "meta": [{"index":0,"metrics":["timestamp"]},{"index":1,"metrics":["sys.cpu.nice","sys.cpu.nice1"],"commonTags":{"host":"web01","dc":"lga"},"aggregatedTags":[]}]
        }
    ],
    "query": {
       ······
    }
}
        

九、trees详解

opentsdb2.0版本引入了tree的概念,tree可以将一些时间序列组织起来使其具有层次结构,和文件系统一样,tree中的叶子类比于文件系统的文件,tree中的branch类比于文件系统的文件夹,还可以继续在里面创建新的文件夹。其相关定义可参考官网。

在tsdb中创建一棵树步骤如下:

  1. 首先创建一棵树,此时数的enable属性为false。
  2. 为这棵树定义一些规则,数的形状和数据是由这些规则确定。
  3. 可以通过/api/tree/test接口对这棵树进行测试,看其接口是否满足要求。
  4. 将树的enable设为true。
  5. 运行./tsdb uid treesync扫描TSMeta中的全部对象,将符合条件的时间序列加入到树中。注意:若需要每创建一个TSMeta对象时,都试图将对象加入到enable tree中,那么在启动tsdb时需要加上 tsd.core.tree.enable_processing=true 配置。

现在按照上面的流程进行操作实际一遍,首先对时间序列、数的规则进行说明。

现在我们有如下的时间序列数据:

opentsdb 写入和查询详细讲解_第1张图片
图1

这些时间序列需要满足如下规则(rules),level表示数的第几层,order表示同一level的不同rule有不同的优先级。level 0 有两个rule,当满足order为0的rule时,会跳过order为1的rule;反之order为1的rule就会生效。

opentsdb 写入和查询详细讲解_第2张图片
图2

基于上面的时间序列和tree的规则,可以得到下面的tree:

opentsdb 写入和查询详细讲解_第3张图片
图3

下面按照步步骤对这棵树进行生成:

  1. 写入图一中的输入,请求body此处略。
  2. 创建一棵树,post接口为 /api/tree,请求body如下:
{"name":"Network","description":"","notes":"","rules":null,"created":1368964815,"strictMatch":false,"storeFailures":false,"enabled":false}

创建成功后 可以用 get方式请求 /api/tree 接口查询tree的相关信息,并可以获得新创建tree的id,treeId下面也会用到。

  1. 利用接口 /api/tree/rule接口,依次创建图2中的4个rule,请求body分别如下:

{"type":"tagk","field":"dc","description":"a tagk named data center","level":0,"order":0,"treeId":1}

{"type":"tagk","field":"host","description":"a tagk named host","regex":".*\\.(.*)\\.mysite\\.com","level":0,"order":1,"treeId":1}

{"type":"tagk","field":"host","description":"a tagk named host","separator":"\\.","level":1,"order":0,"treeId":1}

{"type":"metric","description":"metric","separator":"\\.","level":2,"order":0,"treeId":1}

  1. 利用/api/tree/test 接口测试我们新创建的tree,get请求有两个参数:
参数名 意义
treeId 用于指定测试的tree
tsuids 指定时间序列,试图将这些时间序列放入这颗树中进行测试,多个tsuid以 ","相隔

这里的tsuids当然是指图1中时间序列对应的tsuid,可以用 /api/query接口进行查询。
/api/tree/test会返回这些时间序列基于这棵树的层次关系,若这个层次结构不满足需求则需要对rule进行修改,若满则需求则可进行下一步。

  1. 在tsdb的build文件夹运行 ./tsdb uid treesync 命令,它会扫描全部的 tsdb-uid,将符合条件的序列加入到tree的结构中。

至此这棵树的定义就已经完成,可以用 /api/tree/branch 接口对tree的分支进行查询,查询的参数有两个:

参数名 意义
treeid tree的id
branch branch的id

两个参数只需要一个,当传递treeid时,就会返回root branch的信息。当只传递branch时,就会返回对应branch的信息。当两个参数都传递时,参数treeid就会被忽略。

你可能感兴趣的:(opentsdb 写入和查询详细讲解)