openTSDB详解之HBase Schema

openTSDB详解之HBase Schema【待完善】

本文译自 http://opentsdb.net/docs/build/html/user_guide/backends/hbase.html

1.1 Data Table Schema

默认情况下,所有的openTSDB数据点被存储在单个大表中,这个大表的就是tsdb。这将利用HBase的排序及分区功能。所有的值被存储在叫做t的列族(column family)中。
Row key——Row keys字节数组(byte[]),由optional salt,metrics UID,a base timestamp,the UID for tagk/v pairs组成:[salt]<metric_uid><timestamp><tagk1><tagv1>[....<tagkN><tagvN>]。默认情况下,UIDs使用3字节编码。
(从版本2.2开始)salting功能默认开启,第一个字节是一个hash的salt ID,这么做是为了更好地将数据分布存储在多个regions和/或是region server上。
时间戳是一个以秒为频率的Unix epoch值,编码长度为4字节。行被分解成小时增量保存,在每行中由时间戳反应。因此,每个时间戳将会被规范化为小时值,例如:2013-01-01 08:00:00这是为了避免在单个行中填满太多数据,因为这样将会影响region 分布【这个原因是什么?为啥会影响region分布?】。同样,因为HBase按照row key排序,针对不同的查询,相同metric,time bucket,但是不同的tags,将会被分到相同的组以进行高效的查询。
一些由十六进制表示的,unsalted(可以直接理解为“未加盐处理的”)的row key的例子如下:

00000150E22700000001000001
00000150E22700000001000001000002000004
00000150E22700000001000002
00000150E22700000001000003
00000150E23510000001000001
00000150E23510000001000001000002000004
00000150E23510000001000002
00000150E23510000001000003
00000150E24320000001000001
00000150E24320000001000001000002000004
00000150E24320000001000002
00000150E24320000001000003

其中:

00000150E22700000001000001
'----''------''----''----'
metric  time   tagk  tagv

这代表单个metric,但是在三个小时里有四个时间序列【这里该如何理解? => 意思应该是每个小时里的四个时间序列, 三个小时分别是:50E2270050E2351050E24320】。注意,这里有一个包含两个tags的时间序列。

00000150E22700000001000001000002000004
'----''------''----''----''----''----'
metric  time   tagk  tagv  tagk  tagv

Tag names(tagk)以字母表的顺序存储 在存储row key之前,Tag names(tagk)需要按照字母顺序排序,所以值为host的tag始终存储在row key/TSUIDowner的前面。

1.2 Data point Columns

到目前为止,最普通的列(the most common column)是数据点。当数据被发往TSD中进行存储时,这些才是实际存储的值。

  • Column Qualifiers[列限定符]:列限定符以行为基准时间的偏移值编码的2或者4字节值,并且flags决定值是否是一个整数或者是一个小数值 列限定符由2-4字节构成,从行的基准时间偏移量处开始编码。列限定符编码从row 基准时间的偏移量同时format以及数据存储的长度。(原文:Qualifiers encode an offset from the row base time as well as the format and length of the data stored.)
    具有2字节限定符的列有以秒为单位的偏移量。 (原文:Columns with 2 byte qualifiers have an offset in seconds.) 限定符的前12位代表一个整数,这个整数是行键中时间戳开始的一个整数(原文:The first 12 bits of the qualifier represent an integer that is a delta from the timestamp in the row key)。例如:如果row key被规范化为1292148000并且在时间点为1292148123时到来,那么记录的delta将会是123。最后的4位是格式化标志(原文:The last 4 bits are format flags)。
    使用4字节限定符的列有毫秒级的偏移量。 限定符的前4位将会总是被设置为十六进制的1或者F。接下来的22位编码成毫秒级的偏移量,且是一个无符号整数。接下来的2位被保留,最后4位是格式标志。【译者注:下文会对这4位进行详细介绍】
    上述任一的列类型的最后4位描述了存储的数据。(The last 4 bits of either column type describe the data stored)。第一位是一个标志位,表示值是一个整数还是一个浮点数:值为0时代表是一个整数,为1时表示是一个浮点数。最后的3位表示数据的长度,偏移量为1The last 3 bits indicate the length of the data, offset by 1。000表示1个字节值,010表示一个长度为2字节的值。长度必须反应一个值1,2,4,8。任何其它值都是错误的。
    例如:0100意味着列值是一个长度为8字节有符号整数。1011表示值是一个长度为4字节的浮点数,所以在时间戳为1292148123的其数据点值为:4294967296,将会有一个限定符0000011110110100或者是07B4【十六进制】。
    Column Values :1到8字节的编码,表示限定符值(原文:1 to 8 bytes encoded as indicated by the qulifier flag

1.3 Compactions

如果一个TSD的压缩功能被开启,在它的基础小时数据已经被传递或者一个查询已经在该行上运行完成之后,那么该行的数据将会被压缩(原文: If compactions have been enabled for a TSD, a row may be compacted after it's base hour has passed or a query has run over the row)。压缩的列仅仅压缩所有的数据点到一起,为了减少不同的数据点所占的开销。为了速度性能,在刚开始写的时候,数据被写入各自的列中,然后为了存储效率采取压缩。一旦一行被压缩,独立的数据点会被删除。稍后,数据可能会被写回到行并且可能会被再次压缩。

Note

与HBase的压缩相比,在范围和定义上,openTSDB压缩过程是完全独立的。

Column Qualifiers:对于一个压缩列的列限定符 将会总是一个偶数个字节,并且这个列限定符只是该行中每个数据点的限定符的拼接。因为我们知道每个数据点的限定符的长度是2字节,它只需要简单的将其分割。一个列限定符的16进制表示可能看起来像是07B407D4
Column Values:列值同样也是所有独立数据点值拼接而来的。限定符首先被分裂,以及每个数据点flags决定是否分析器需要消耗4或者8字节。

1.4 Annotations or Other Objects

一行可能存储关于时间序列的内部数据点的注释(原文:A row may store notes about the timeseries inline with the datapoints)。Object与数据点不同的地方,在于限定符中有奇数个字节。
Column Qualifiers:限定符是3或者5字节,其中第一个字节是ID,代表column是一个限定符。第一个字节始终有一个十六进制值0x01,为了注释(将来的对象类型可能有一个不同的前缀)。剩下的字节编码为时间戳delta,来自行基准时间以一种类似数据点的方式,尽管没有flags(原文:The remaining bytes encode the timestamp delta from the row base time in a manner similar to a data point, though without the flags)。如果限定符长度是3字节,偏移量是以秒为单位。如果限定符长度为5字节,那么偏移量是毫秒级。因此,如果我们在1292148123上记录一个注释,delta值将会是123,并且限定符的十六进制表示是01007B
Column values:注释的值是UTF-8编码的JSON对象。不要直接修改值。字段的顺序是重要的,否则会影响CAS调用(原文:The order of the fields is important,affecting CAS calls.

1.5 Append Data points

openTSDB 2.2引进了使用追加(append)的方式将数字点写入到openTSDB中,而不是传统的put方式。通过在单个列中为一行写入所有数据,可以节省HBase中的空间,(正是因为使用了追加的方式,才)支持TSD压缩,同时避免了将大量数据读回TSD并重新写入HBase时出现的问题。(原文:This saves space in HBase by writing all data for a row in a single column, enabling the benefits of TSD compactions while avoiding problems with reading massive amounts of data back into TSDs and re-writing them to HBase)。缺点是schema与正常的数据点是不兼容的,并且要求HBase region servers具备更好的CPU性能,因为他们需要为每个值执行读,修改,写等操作【译者注:这里介绍了优缺点】。
Row Key—与普通的值相同
Column Qualifier:限定符始终以前缀0x05开头,是一个来自基准时间的,长度为2字节的偏移量。例如:0x050000
Column Values:每个列值是原始数据点(original data point qualifier)的限定符偏移量拼接而成,以及值的格式:<offset1><value1><offset2><value2>...<offsetN><valueN>。值能够出现在任何顺序,以及在查询的时候被排序(使用选项去重写排序后的结果到HBase中)。

1.6 UID Table Schema

一个独立的,较小【译者注:较小是相对于上文的tsdb表来说】的表叫做—tsdb-uid,存储UID的双向映射。存在两列,一列叫做name,存储映射uid->name;另外一个叫做id,存储映射name->uids。列族中的每行将至少有三列映射值。标准的列限定符如下:

  • metrics :映射metric->UIDs
  • tagk: 映射tag names->UIDs
  • tagv: 映射tag values -> UIDs
    如果配置过元数据的存储位置,name 这个列族同时也包括其它的元数据列。

第二部分将会给出笔者机器上的tsdb-uid

hbase(main):002:0> desc 'tsdb-uid'
Table tsdb-uid is ENABLED                                                                                                                                                                                  
tsdb-uid                                                                                                                                                                                                   
COLUMN FAMILIES DESCRIPTION                                                                                                                                                                                
{NAME => 'id', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLO
CKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}                                                                                                                                         
{NAME => 'name', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', B
LOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}                                                                                                                                       
2 row(s) in 0.1370 seconds
1.6.1 id Column Family【列族id】

Row key:这将是分配给UID的字符串译者注:rowkey是一个字符串,而不是一个整数?】。例如,对于一个metric,我们可能有一个值:sys.cpu.user或者一个tag value,它的id可能是42。
Column Qualifiers:上述标准列类型之一【译者注:哪些?】
Column Value:默认情况下,是一个长度为3字节的无符号整数,反映分配给字符串的UID,对于列类型(原文:An unsigned integer encoded on 3 bytes by default reflecting the UID assigned to the string for the column type)。如果在源码中,UID长度有一个改变,宽度可能会改变。

1.6.2 name Column Family【列族name】

Row key:默认情况下,长度为3字节的无符号整数UID。如果UID长度在源码中改变了,宽度将会不同。
Column Qualifiers:上述的标准列类型,或者是metrics_metatagk_metatagv_meta之一。
Column Value:对于上述标准的限定符,分配给UID的字符串(即是column value)。对于一个*_meta列,值将是UTF-8编码,JSON格式UIDMeta对象的一个字符串。不要修改列值在openTSDB之外。字段的顺序是重要的,影响CAS调用。

1.7 UID Assignment Row

id列族中,只有一个单行,该行只有单个字节\0x00(原文:Within the id column family is a row with a single byte key of \x00)。这是一个UID行,被用于增加合适的列类型(metrics,tagk,tagv)当一个新的UID被分配。列值是8字节符号整数,并且反应了最大UID,为每个类型分配的UID。在赋值时,openTSDB在正确的列上调用HBase的原子增量命令来获取新的UID。

1.8 Meta Table Schema

这个表是存储在openTSDB 中不同的时间序列的一个索引,并且包含每个时间序列及数据点数目的元数据信息(meta-data)。 注意:如果openTSDB已经被配置为跟踪meta-data,或者用户通过API创建一个TSMeta 对象,那么数据仅仅 才会被写到这个表中。(该表)仅使用一个列族(name),以及目前只有两种类型的列,meta 列以及counter 列。

1.8.1 Row key

这个和data point存储的row key相似,只不过没有时间戳。例如:<metric_uid><tagk1><tagv1>[...<tagkN><tagvN>]。所有的列类型均使用这个rowkey

1.8.2 TSMeta Column

类似UIDMeta对象,这些列 【译者注:哪些列?还是特指TSMeta列?】 应该指的是 【译者注:TSMeta ColumnCounter Column】以UIT-8编码的json对象存储。这个列名始终是ts_meta。不要在openTSDB之外修改这些列值,因为它可能会终止CAS 调用.

1.8.3 Counter Column

这些列是原子增量,用于计算某个时间序列存储数据点的数目。限定符是ts_counter以及值是一个8字节的带符号整数。

1.9 Tree Table Schema

这张表【译者注:即标题中的Tree Table】的看起来像是是一个索引,与文件系统相类似,它构建时间序列成一个等级结构,为了使用诸如Graphite以及其它仪表盘(原文:This table behaves as an index, organizing time series into a hierarchical structure similar to a file system for use with tools such as Graphite or other dashboards)。一个tree由一系列规则定义,这个规则是处理TSMeta对象在等级结构的哪里, 如果满足条件,那么一个时间序列将会出现。
每棵树被分配一个由1开头的无符号整数组成的唯一ID,对于第一棵树。所有行与这棵树相关的地方在于:行键会用这棵树的前缀作为编码,占用两个字节。例如:\x00\x01对于UID 1。【译者注:也就是说,\x00\x01会是这个row key的前缀。】

Row key

树中定义的行键,使用树的ID作为键中的起始两个字节(原文:Tree definition rows are keyed with the ID of the tree on two bytes.)。与树的定义相关的列,同时root 的分支,均会出现在行中(原文:Columns pertaining to the tree definition, as well as the root branch, appear in this row)。定义由用户生成。
两个特殊的行必须包括。他们的键是<tree ID>\x01用于冲突行(collisions row)的行键,以及<tree ID>\0x2对于不匹配(not matched row)的行键。它们都是在树处理的过程中生成,稍后会再次描述。
剩下的行就是普通分支以及包涵关于层级信息的叶子行 。行的键是<tree ID><branch ID>branch ID是一个分支显示名的哈希拼接。例如,如果我们有一个压平的分支【译者注:就是将所有分支放在同一水平线上分析】dal.web01.myapp.bytes_sent,每个分支名字由一个点号独立开来,我们将有3层分支。dal,web01以及myapp。叶子将会被命名成bytes_sent以及链接到一个TSUID(原文:links to TSUID)。在java中,每个分支名的哈希返回4字节整数,并且为了可读,将其转换成16进制:

  • dal = x00x01x83x8F
  • web01 = x06xBCx4Cx55
  • myapp = x06x38x7CxF5
    如果这个分支属于树1,那么dal的行键将会是\x00\x01\x00\x01\x83\x8F【译者注:Tree UID +dal UID】。分支myapp将会是\x00\x01\x00\x01\x83\x8F\x06\xBC\x4C\x55\x06\x38\x7C\xF5【译者注:Tree UID +dal UID + web01 UID】。这个模式允许遍历通过提供一个行键过滤使用一个前缀,包括tree ID以及当前分支层级以及一个通配符去匹配任何孩子分支层级树(通常仅仅是往下一层遍历)
Tree Column

一棵树是定义成UTF-8编码的json对象,在tree列 ,一棵树的行中(定义由tree’s ID)(原文:A Tree is defined as a UTF-8 encoded JSON object in the tree column of a tree row (identified by the tree's ID).)。对象包涵描述以及配置关于处理时间序列的设置,通过tree(原文:The object contains descriptions and configuration settings for processing time series through the tree)。不要修改这个对象在openTSDB之外,因为这将打破CAS的调用。

Rule Column

在树的行中,有0或者更多个规则列,定义一个在时间序列上特殊的处理任务。这些列同样是UTF-8编码的json对象,以及通过CAS调用修改(这些对象信息)。列id的格式是rule:<level>:<order><level>是rule的主要处理顺序在以1开头的规则集中并且order是rule以0开头的处理顺序在一个给出的层级中【译者注:原文可能有误,这里我根据自己理解,对原文稍加修改。】。例如:rule:1:0定义一个在level=1,order=0中的规则。

Tree Collision Column

如果tree中开启了碰撞存储,对于每个时间序列,将有一列专门记录此碰撞信息,这将会创建一个叶子 ,这个叶子已经创建对于之前的时间序列。这些列被用于调试rule sets以及仅仅出现在一棵树的碰撞行中。碰撞列的格式是tree_collision:<tsuid>,TSUID是一个字节数组,代表时间序列的标识符。这允许一个简单的getRequest调用去决定是否一个主要的时间序列没有出现在一棵tree中由于碰撞。 碰撞列的值是字节数组,将被作为一个叶子而记录。

Not Matched Column

与碰撞列类似,当tree中开启不匹配列时,这个列将会记录每个不能匹配任何规则集中规则的数据时间序列,因此该事件序列不会出现在树中。这些列仅仅出现在一棵树的不匹配行中。列的格式是tree_not_matched:<TSUID>,TSUID是一个代表时间序列标识符的字节数组。一个不匹配的列的值是TSUID的一个字节数组,不能匹配一个规则。

Branch Column

分支列中有一个叫做branch的列,以及包含一个utf-8编码的json对象,这个json对象描述的是当前分支以及任何可能存在的子分支。一个分支列可能出现在除了碰撞行或不匹配行之外的任何行。在树定义行中的分支是root 分支,并且链接到孩子分支的第一层。这些链接被用于遍历层级。

Leaf Column

叶子是映射到具体的时间序列,并且代表一个层级的结束。叶子列的限定符格式是leaf:<TSUID>,TUID是一个字节数组代表时间序列标识符。叶子值是一个描述叶子信息的,UTF-8编码的json对象。叶子可能出现在任何行中而不是碰撞或者是不匹配的行中。

Rollup Tables Schema

在openTSDB2.4 版本中,Rollup Tables Schema,是rollup 以及pre-aggregation tables的概念。然而TSDB做了一个十分伟大的工作:存储你想要的raw values,在很大的raw data之上的广泛的跨度查询能够将查询速度降到龟速以及潜在的OOM a JVM(原文:While TSDB does a great job of storing raw values as long as you want, querying for wide timespans across massive amounts of raw data can slow queries to a crawl and potentially OOM a JVM)。相反,各自的时间序列能够通过时间roll up(或者downsampled),以及存储作为分割值允许以一个较低的频率扫描较宽泛的时间序列。另外,对于高基数的metrics,pre-aggregte组能够被存储,用于显著地提升查询速度。
有三种类型去rolled up【译者注:我的理解是:归并】数据:

  • Rollup:这是一个downsampled值,遍布时间对于单个时间序列。与在查询中使用一个downsampler相似,一个时间序列可能每分钟有一个数据点,但可以通过使用sum聚合函数被downsampled到每小时一个数据点。在这种情况下,rolled up的结果值是60个值的总和。例如:如果每1分钟的时间点的值都是1,那么结果的rollup值将会是60。
  • Pre-Aggregate:对于一个有高基数(许多唯一的tag values)metric,扫描所有的时间序列可能是非常费时的。例如:在metric=system.interface.bytes.out,有10000个主机,分布在5个数据中心上。如果用户经常看数据中心总的输出数据,(用户查询与aggregation=sum以及data_center=*相似),那么pre-calculating总和将会计算5个数据点,每个时间周期来自存储,而不是10k。结果的pre-aggregate将会有一个不同的tag set而不是raw 时间序列。在上述的例子中,每个时间序列将可能有一个tag=host,与一个data_center tag。在pre-aggregation之后,host tag将会被取消,仅仅留下data_center这个tag。
  • Rolled-up Pre-Aggregate:预聚合的数据同样可以及时rolled up,与raw time series 相似。这能提升查询速度对于广泛时间跨度在预聚合的数据上。

Configuration

备忘录:Settle on a config(原文:TODO - Settle on a config)。rollup配置由一个表名,间距,以及rollup 间距组成。raw pre-aggs能够被存储在数据表中或是rollup 表中。

Pre-Aggregate Schema

在openTSDB的实现中,当rollups是被开启时,一个新的、用户配置的tag会被添加到所有的时间序列中。默认的key是_aggregate,其值是raw或是一个聚合函数(原文:The default key is _aggregate with a value of raw or an aggregation function.)。tag被用于从raw(original)值中区分pre-aggregated data。因此,pre-aggregated data要么以与原始的时间序列相同的方式存储到原始数据表,要么存放在和原始数据不同的表中,这样可以实现更好的查询性能。

Rollup Schema

为了避免存储的schema 冲突,同时也是为了更好的性能的查询,Rolled up的数据必须被存储在与raw data隔离的一个表中。【译者注:注意上面的Pre-Aggregate Schema的表,既可以和原数据一起存放,又可以单独存放
Row Key:用于rollup的row key 在格式上与源表相同(原文:The row key for rollups is in the same format as the original data table)。
Column Qualifier:对于rolled up数据列是不同的,以及由<aggregation_function>:<time offset><type + length>组成,aggregation function是一个大写字符串由函数名 用于聚合rollup 以及时间偏移基于行基准时间的偏移量 ,type+length:描述了列值编码。

  • Aggregation Function:这是一个函数名诸如Sum ,count,max,或者是min
  • Time Offset:这是一个基于rollup表配置的偏移量,通常是2字节。偏移并非是一个指定的数字秒或者分钟,来自行键基础偏移量,相反,它是一个偏移量间距的索引。例如:如果表配置为存储1天的数据,以每行一小时的频率,那么基准的时间戳行键的将会对齐到日期边界(在Unix epoch timestamps)。那么将会有一个24个偏移量(每天中的每小时为1)对于每行。一个给出日期在午夜的数据点将会将会有一个偏移量0,然而23:00将会有一个偏移值22。因为rollup timestamps对齐到时间边界,限定符能够保存相当的空间。
    • Type and length:与original data 表相似,偏移量的最后的4位包含数据值编码,包含它的长度以及是否是一个浮点值等信息。
      如下是一个例子:对于每天1小时间隔表的列(column qualifier)看起来像是这样:
SUM:0010
'-''---'
agg offset

aggregation是sum,偏移量是1,以及长度是1字节的整数值。
Column Value:值是与主要的数据表相同的。

TODOS:一些进一步的工作需要

  • 压缩/追加:目前的模式不支持压缩或者追加数据类型【译者注:那么支持压缩或者追加数据么?】。这些能够通过表示一个单列每个聚合函数(例如:sum,count)以及存储与主表中相似的列数据和偏移量,来实现。
  • 另外的数据类型:目前仅仅数字值被写入到pre-agg 以及 rollup表中。我们需要支持rolling up annotations以及其他类型的数据。
============ ======== update log【2018-10-27】 ====================

今天在读openTSDB源码的时候,发现TSDB类中存在如下的代码,这个config是一个配置对象,其enable_appends()属性值就是用来判断是否允许追加数据点的功能。

  • TSDB类
if (config.enable_appends()) {//如果开启追加功能的话
          final AppendDataPoints kv = new AppendDataPoints(qualifier, value);
          final AppendRequest point = new AppendRequest(table, row, FAMILY, 
              AppendDataPoints.APPEND_COLUMN_QUALIFIER, kv.getBytes());
          result = client.append(point);
        } else {
          scheduleForCompaction(row, (int) base_time);
          final PutRequest point = new PutRequest(table, row, FAMILY, qualifier, value);
          result = client.put(point);
        }
  • Config类
  /** @return whether or not to write data in the append format
   * 是否允许以追加的方式写入数据
   * */
  public boolean enable_appends() {
    return enable_appends;
  }
  • Config类
  /** tsd.storage.enable_appends
   * */
  private boolean enable_appends = false;  

可以看到其默认值为false。

==================== update on 2018 11 05==============================

2.表展示

如下针对opentsdb中的系统表,我给出我机器中的表数据如下:

  • tsdb-uid表
hbase(main):012:0> scan 'tsdb-uid'
ROW                                                 COLUMN+CELL                                                                                                                                            
 \x00                                               column=id:metrics, timestamp=1535982247189, value=\x00\x00\x00\x00\x00\x00\x00\x03                                                                     
 \x00                                               column=id:tagk, timestamp=1535982247222, value=\x00\x00\x00\x00\x00\x00\x00\x03                                                                        
 \x00                                               column=id:tagv, timestamp=1537103490101, value=\x00\x00\x00\x00\x00\x00\x00\x06                                                                        
 \x00\x00\x01                                       column=name:metrics, timestamp=1531479245132, value=mytest.cpu                                                                                         
 \x00\x00\x01                                       column=name:tagk, timestamp=1531479245162, value=host                                                                                                  
 \x00\x00\x01                                       column=name:tagv, timestamp=1531479245189, value=server4                                                                                               
 \x00\x00\x02                                       column=name:metrics, timestamp=1535891521172, value=metric-t                                                                                           
 \x00\x00\x02                                       column=name:tagk, timestamp=1535891521198, value=chl                                                                                                   
 \x00\x00\x02                                       column=name:tagv, timestamp=1531479264404, value=server5                                                                                               
 \x00\x00\x03                                       column=name:metrics, timestamp=1535982247205, value=csdn                                                                                               
 \x00\x00\x03                                       column=name:tagk, timestamp=1535982247230, value=accessNumber                                                                                          
 \x00\x00\x03                                       column=name:tagv, timestamp=1531485413194, value=s485276                                                                                               
 \x00\x00\x04                                       column=name:tagv, timestamp=1535891521217, value=hqdApp                                                                                                
 \x00\x00\x05                                       column=name:tagv, timestamp=1535982247253, value=cs                                                                                                    
 \x00\x00\x06                                       column=name:tagv, timestamp=1537103490275, value=Firminal                                                                                              
 Firminal                                           column=id:tagv, timestamp=1537103490289, value=\x00\x00\x06                                                                                            
 accessNumber                                       column=id:tagk, timestamp=1535982247235, value=\x00\x00\x03                                                                                            
 chl                                                column=id:tagk, timestamp=1535891521203, value=\x00\x00\x02                                                                                            
 cs                                                 column=id:tagv, timestamp=1535982247259, value=\x00\x00\x05                                                                                            
 csdn                                               column=id:metrics, timestamp=1535982247213, value=\x00\x00\x03                                                                                         
 host                                               column=id:tagk, timestamp=1531479245177, value=\x00\x00\x01                                                                                            
 hqdApp                                             column=id:tagv, timestamp=1535891521224, value=\x00\x00\x04                                                                                            
 metric-t                                           column=id:metrics, timestamp=1535891521182, value=\x00\x00\x02                                                                                         
 mytest.cpu                                         column=id:metrics, timestamp=1531479245145, value=\x00\x00\x01                                                                                         
 s485276                                            column=id:tagv, timestamp=1531485413204, value=\x00\x00\x03                                                                                            
 server4                                            column=id:tagv, timestamp=1531479245192, value=\x00\x00\x01                                                                                            
 server5                                            column=id:tagv, timestamp=1531479264407, value=\x00\x00\x02      

可以看到 id 列和name 列的value刚好是一一对应的。
\x00\x00\x03 column=name:metrics, timestamp=1535982247205, value=csdn
csdn column=id:metrics, timestamp=1535982247213, value=\x00\x00\x03
因为tsdb-uid表存储的就是一个id<->name的一 一映射。

4.问题答疑

4.1 1楼童鞋问的问题

因为之前我的opentsdb没有开启meta-data这个功能,导致tsdb-data表为空。然后我停了正在运行的openTSDB,在配置文件opentsdb.conf中添加了tsd.core.meta.enable_tsuid_incrementing=truetsd.core.meta.enable_tsuid_tracking=true。再次启动时,使用hbase shell查看如下

  • 查看表数据
hbase(main):001:0> scan 'tsdb-meta'
ROW                                                 COLUMN+CELL                                                                                                                                            
0 row(s) in 0.2920 seconds
  • 查看表结构
hbase(main):007:0> desc 'tsdb-meta'
Table tsdb-meta is ENABLED                                                                                                                                                                                 
tsdb-meta                                                                                                                                                                                                  
COLUMN FAMILIES DESCRIPTION                                                                                                                                                                                
{NAME => 'name', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', B
LOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}                                                                                                                                       
1 row(s) in 0.3470 seconds

即使修改了配置文件,因为之前的uid已经生成,所以这里不会再生成meta-data信息。于是我单写了若干条新的tag pair数据,再次查看tsdb-meta表时,如下:

  • 查看表数据
hbase(main):004:0> scan 'tsdb-meta'
ROW                                                 COLUMN+CELL                                                                                                                                            
 \x00\x00\x03\x00\x00\x03\x00\x00\x07               column=name:ts_ctr, timestamp=1541425687506, value=\x00\x00\x00\x00\x00\x00\x00\x01                                                                    
 \x00\x00\x03\x00\x00\x03\x00\x00\x08               column=name:ts_ctr, timestamp=1541425687506, value=\x00\x00\x00\x00\x00\x00\x00\x01                                                                    
2 row(s) in 0.0170 seconds

发现tsdb-meta表已经有数据写入了。其中的name:ts_ctr就是这个counter值。其在源码中的定义如下:

  /** The cell qualifier to use for timeseries meta
   *  针对timeseries meta使用的单元qualifier
   * */
  private static final byte[] COUNTER_QUALIFIER = "ts_ctr".getBytes(CHARSET);

在我采集了一会儿数据之后,再次获取这个表中的数据时,发生了如下变化:

*  ROW                                     COLUMN+CELL
*  \x00\x00\x04\x00\x00\x03\x00\x00\x08    column=name:ts_ctr, timestamp=1541495562451, value=\x00\x00\x00\x00\x00\x00\x02\xAA

可以看到counter的值由0x0001 -> 0x02AA,即采集了 682 个数值。
根据官网的文档说明,TSMeta Column中存储的是类似UIDMeta对象的值,我猜测是TSMeta对象值。该类在源码中的定义如下:
Timeseries Metadata is associated with a particular series of data points and includes user configurable values and some stats calculated by OpenTSDB.
对于第二个问题相当于是问这里的一个时间序列的数据点的范围是什么?
其中http://opentsdb.net/overview.html 对time_series的介绍(如下示)

 In OpenTSDB, a time series data point consists of:

    A metric name.
    A UNIX timestamp (seconds or millisecinds since Epoch).
    A value (64 bit integer or single-precision floating point value), a JSON formatted event or a histogram/digest.
    A set of tags (key-value pairs) that describe the time series the point belongs to.

可以知道:a time series data point <=> a metric + a unix timestamp + a value + a set of tags,如果上述的这些有一个方面不一样,则是一个不同的data point

===== update on 2018 11 06 ====

昨天一直很郁闷为什么我的tsdb-meta表中没有ts_meta列,该表的数据如下:

hbase(main):007:0> desc 'tsdb-meta'
Table tsdb-meta is ENABLED                                                                                                                                                                                 
tsdb-meta                                                                                                                                                                                                  
COLUMN FAMILIES DESCRIPTION                                                                                                                                                                                
{NAME => 'name', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', B
LOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}                                                                                                                                       
1 row(s) in 0.3470 seconds

感觉始终是配置文件(opentsdb.conf)问题,于是我又去官网寻找信息,终于找到一篇文章:http://opentsdb.net/docs/build/html/user_guide/metadata.html,这里面介绍了TSMeta这个对象的信息。这篇文章对应的我的译文链接是:https://blog.csdn.net/liu16659/article/details/81295750 。接着又找到opentsdb的配置信息:http://opentsdb.net/docs/build/html/user_guide/configuration.html。终于知道如果要开启meta功能,是需要在配置文件中配置:

# --- control tsdb_uid -> ts_meta
tsd.core.meta.enable_realtime_uid = ture

如果想开启data points的实时计数功能,则需要在配置文件中配置:

# --- control counter ts_ctr -
tsd.core.meta.enable_tsuid_incrementing=true
tsd.core.meta.enable_tsuid_tracking=true
tsd.core.meta.enable_realtime_ts=true

配置完上述的信息之后,再写入一个新的metric信息,再次查看tsdb-meta中的数据,如下:

hbase(main):081:0> scan 'tsdb-meta'
ROW                                                 COLUMN+CELL                                                                                                                                           
 \x00\x00\x03\x00\x00\x03\x00\x00\x05               column=name:ts_ctr, timestamp=1541500555256, value=\x00\x00\x00\x00\x00\x00\x01\xDE                                                                   
 \x00\x00\x03\x00\x00\x03\x00\x00\x06               column=name:ts_ctr, timestamp=1541487859821, value=\x00\x00\x00\x00\x00\x00\x00\x01                                                                   
 \x00\x00\x03\x00\x00\x03\x00\x00\x07               column=name:ts_ctr, timestamp=1541426224876, value=\x00\x00\x00\x00\x00\x00\x00\x01                                                                   
 \x00\x00\x03\x00\x00\x03\x00\x00\x08               column=name:ts_ctr, timestamp=1541500555272, value=\x00\x00\x00\x00\x00\x00\x01\xDD                                                                   
 \x00\x00\x04\x00\x00\x03\x00\x00\x07               column=name:ts_ctr, timestamp=1541495601500, value=\x00\x00\x00\x00\x00\x00\x02\xAD                                                                   
 \x00\x00\x04\x00\x00\x03\x00\x00\x08               column=name:ts_ctr, timestamp=1541495601503, value=\x00\x00\x00\x00\x00\x00\x02\xAD                                                                   
 \x00\x00\x05\x00\x00\x03\x00\x00\x05               column=name:ts_ctr, timestamp=1541500656957, value=\x00\x00\x00\x00\x00\x00\x00\x01                                                                   
 \x00\x00\x05\x00\x00\x03\x00\x00\x05               column=name:ts_meta, timestamp=1541500657889, value={"tsuid":"000005000003000005","displayName":"","description":"","notes":"","created":1541500656,"c
                                                    ustom":null,"units":"","dataType":"","retention":0,"max":"NaN","min":"NaN"}                                                                           
 \x00\x00\x05\x00\x00\x03\x00\x00\x08               column=name:ts_ctr, timestamp=1541500657002, value=\x00\x00\x00\x00\x00\x00\x00\x01                                                                   
 \x00\x00\x05\x00\x00\x03\x00\x00\x08               column=name:ts_meta, timestamp=1541500657889, value={"tsuid":"000005000003000008","displayName":"","description":"","notes":"","created":1541500656,"c
                                                    ustom":null,"units":"","dataType":"","retention":0,"max":"NaN","min":"NaN"}                                                                           
8 row(s) in 0.0320 seconds

这次可以看到,数据终于出来了,如果嫌弃不好阅读,可以使用HTTP api进行阅读,如下:
openTSDB详解之HBase Schema至此,各个tsdb-meta表数据的各个列都分析完毕。

上一篇:metadata流失与带参装饰器


下一篇:SpringBoot整合Redis