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,但是在三个小时里有四个时间序列【这里该如何理解? => 意思应该是每个小时里的四个时间序列, 三个小时分别是:50E22700
,50E23510
,50E24320
】。注意,这里有一个包含两个tags的时间序列。
00000150E22700000001000001000002000004
'----''------''----''----''----''----'
metric time tagk tagv tagk tagv
Tag names(tagk)以字母表的顺序存储 在存储row key之前,Tag names(tagk)
需要按照字母顺序排序,所以值为host的tag始终存储在row key/TSUID
为owner
的前面。
1.2 Data point Columns
到目前为止,最普通的列(the most common column
)是数据点。当数据被发往TSD中进行存储时,这些才是实际存储的值。
-
Column Qualifiers
[列限定符]:列限定符以行为基准时间的偏移值编码的2或者4字节值,并且列限定符由2-4字节构成,从行的基准时间偏移量处开始编码。列限定符编码从row 基准时间的偏移量同时format以及数据存储的长度。(原文:flags
决定值是否是一个整数或者是一个小数值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_meta
,tagk_meta
,tagv_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 Column
和Counter 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以及其他类型的数据。
今天在读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。
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=true
和tsd.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
。
昨天一直很郁闷为什么我的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进行阅读,如下:
至此,各个tsdb-meta
表数据的各个列都分析完毕。