opentsdb写入和查询内部实现

前言

前面对tsdb的uid进行了详细介绍,理解了uid的优势和实现原理。现在已经迫不及待的去了解tsdb的写入和查询流程了。Let's start!

write data point (写入数据)

一个 data point 构成如下:

{
        "metric": "metric",
        "timestamp": timestamp,
        "value": value,
        "tags": {
            "tagk1": "tagv1",
            "tagk2": "tagv2"
        }
}

OK!这时候我要上图了:

图 1

大致的流程就是:

  1. 为metric, tags申请uid;
  2. metric_uid,tags_uid和时间戳组成hbase的rowkey。
  3. 将value存储在hbase的tsdb表中。

前一篇博客已经讲述了uid的细节,这里不进行赘述。我们知道hbase表的基本结构是这样子的:

图 2

我们知道最终数据都会落盘到hbase,下面来揭开rowkey, qualifier(即列族下面的列名)的面纱,看看他们的实现细节。

roykey设计

首先,通过 timestamp ,tsdb引入了 basetime 和 time_offset,basetime 就是timestamp 向下取整点得到的结果, time_offset = timestamp - basetime ,如下:

timestamp : 1569508277000 2019-09-26 22:31:17
basetime : 1569506400000 2019-09-26 22:00:00
time_offset = timestamp - basetime = 1877000

rowkey的构成是这样的:

rowkey = metirc_uid + basetime + tagk1_uid + tagv1_uid + tagk2_uid + tagv2_uid ...

  1. uid默认的宽度默认为3byte;
  2. basetime的宽度是4byte。
    basetime原本是一个long型的时间戳,但是它所代表时间的是整点,所以basetime后面五位一定是0,可以将basetime转换为int类型,于是basetime的宽度是4byte。

qualifier设计

tsdb的数据都是存储在同一个列族下,这个列族叫做 t, qualifier就是列族下面的列名:

图 3

qualifier 主要由 time_offset 和 flag 构成。

flag是去描述value的,flag的宽度是4bit。
1.第1 bit 代表value是整数还是浮点数,0是整数,1是浮点数;
2.后面 3bit 代表value的宽度,其只能为 000, 010, 100, 111 ,分别1,2,4,8 byte。

  1. 当timestamp是毫秒级时, qualifier宽度为2byte;
    前 12bit 是time_offset,后面 4bit 是flag。
  2. 当timestamp是秒级时,qualifier宽度为4byte;
    前 4bit 要为1或者F,没有特殊意义;下面 22bit 是time_offset;接着 2bit 保留;最后 4bit 是flag。

好勒,rowkey和qualifier都已经生成,是时候可以将数据存储到hbase中tsdb这张表中,存储的结构可以参考图 3。官方文档也对存储结构进行了详细讲解,可以结合起来参考一下。

compact

当写入新的数据之后,表中的数据如图 3。同一时间序列,一个小时之内的数据都会存储中同一个 row 中,因为他们的生成的rowkey是相同的,只是各自的qualifier不一样。

但是这种存储会有一些问题,不同的点在不同的列名下面,不管是存储和查询都会有额外的开销,于是tsdb会每小时对数据压缩,压缩之后:

图 4

qualifier会被压缩成一个列名,并且value也会合并起来作为一个value存储。在进行写入的时候,写入到不同的列名下是为了加快写入速度。

query data point

Hbase的rowkey是按照顺序存储的,它有一个Scanner,可以指定start key和end key对数据进行扫描。

图 5

扫描的star和end构成如下:

start_rowkey = metric_uid + startTime_baseTime
end_rowkey = metric_uid + endTime_baseTime

在查询数据时,就在start_rowkey和end_rowkey之间查找满足条件的row,并进行返回。

当一行行数据从hbase中返回回来之后,还是byte字节形式,这是还不能直接返回给用户,还需要按照规则进行反序列,这个反序列化的过程可以看做是存储过程的逆过程。

反序列之后可以得到data point真正的timestamp(basetime+offset)。利用Scanner扫描出来的数据并不是全部的数据的timestamp都在查询条件的开始时间和结束时间区间内,是因为Scanner扫描使用baseTime进行扫描的。所以需要进一步对timestamp进行验证。

对于数据聚合,降采样(down sampling)这些步骤是在从hbase查询出来之后进行的。

总结

这篇文章从整体上讲述了opentsdb的写入和查询,也让我们认识了它的内部结构。越是理解了它的内在,就越能明白它的优缺点,接着我们就能很好的使用这个工具。

嘿嘿嘿,就到这里了,下次再见!

你可能感兴趣的:(opentsdb写入和查询内部实现)