openTSDB详解之Trees【待完善】
和metadata一起,openTSDB2.0引进了trees(树)的概念,一个组织时间序列成一个容易导航的结构的分层方法,从而可以被浏览,这与电脑中的文件系统相似。用户能够定义大量trees,使用不同的规则集(原文rule set
),从而将TSMeta对象组织成一个树形结构。然后通过HTTP API endpoint,用户能够浏览结果树。详见/api/tree。
Tree Terminology【树结构中的常用术语】
-
Branch
:每个分支都是树的一个节点。它包含了一系列子节点和叶子,同时也包含一系列父分支【什么叫包含一系列父分支?】。 -
Leaf
:分支的末尾即是叶子,代表的是特殊的时间序列。叶子将会包含一个TSUID值,这个值能够被用于生成一个TSD query。一个分支能够,也可能有多个叶子。 -
Root
:根分支是tree的开始,并且所有的分支从根节点延伸。它的深度为0。 -
Depth
:每当有一个分支被加入到另外一个分支,深度就会增加。
-Strict Matching
:当开启这个功能时,一个时间序列必须在每个规则集的层次中匹配一个规则(原文:When enabled, a timeseries must match a rule in every level of the rule set
)。如果一个或者更多层不能匹配,时间序列将不会被包含在tree中。
-Path
:在继承层次中,本层分支之上的每层分支的名字以及层级。【这个信息组成了path】
Branch
树的每个节点被记录为一个Branch对象。每个branch(对象)包含诸如以下的信息:
-
Branch ID
:分支的ID。这是一个十六进制数,如下所述。 -
Display Name
:branch的名字,通过tree rule set
解析TSMeta对象而成。 -
Depth
:分支在层次结构中的深度。 -
Path
:每个父分支(包括本地分支)的深度以及名字。 -
Branches
:在当前这个分支之下的子分支 -
Leaves
:属于这个分支上的叶子结点
从(树)根开始遍历一个tree,树根(root
)始终有一个ID,这个ID与tree的ID相同。(原文:Navigating a tree starts at the root branch which always has an ID that matches the ID of the tree the branch belongs to.
)根应该有一个或者多个孩子结点,从而能够被使用去遍历到树的下一个层次。每个孩子能够被使用去遍历它们的的孩子,并递归下去。根没有任何的父节点并且depth=0恒成立。如果一棵树仅仅是被定义了或者是开启了,它可能尚未有一个根分支,通过扩展的方式,将不会有任何的子分支。(If a tree has just been defined or enabled,it may not have a root branch yet,and by extension,there won't be any child branches.
)
每个分支将经常有一系列孩子结点。然而,如果一个分支是处于path末尾,它可能不会有任何孩子结点分支,但是它应该有一系列的叶子结点。
Branch IDs and Paths
分支IDs 是十六进制字节编码,与TSUIDs相似,但是它们在格式上稍有不同。分支IDs总是以2字节的tree Id打头。根分支有一个与tree ID相同的branch ID。因此tree 1【注:这里tree 1表示有一棵树,其id为1】的根节点将有一个分支ID为0001。
每个孩子分支有一个DisplayName
【该字段含义可见上】值,并且这个值的hash用于为该branch产生一个32位的整数ID。使用的哈希函数是Java中的java.lang.String
的哈希函数。4字节的整数值是被编码成8位十六进制的字符。例如:对于一个分支来说,如果我们有一个display name:sys
,那么哈希值将会返回102093。TSD将会转换这个值成十六进制的0001BECD。
一个分支ID是由tree ID与当前分支上的每个父分支的ID拼接而成。所以,如果root有一个孩子分支sys,那么该孩子分支的ID将会是:00010001BECD。
假设有一个branch,其display name是cpu,它是分支sys的子分支。cpu返回一个哈希值98728,转换成16进制是000181A8。这个孩子的ID就是00010001BECD00181A8。
以这种方式创建IDs,主要是因为分支函数以及叶子存储,但是也是因为作为一种遍历方法:从树的任何一个结构都能回溯到树根。如果你知道末端分支的路径,并且想向上移动一至多层,这种构建方式是非常有用的。不幸的是,一个深树可能创建很长的分支IDs,所以同时树深不应该超过5-10层。大多数URI请求在达到URI字符约束之前,可以支持最多100个级别的分支。
Leaves
一个唯一的时间序列在一棵树中作为叶子而存在。一个叶子能够出现在树的任何分支上,包括根节点。但是它们将经常出现在一系列分支的低端,这个分支有一或者多个叶子,但是没有子分支。每个叶子包含TSUID,对于每个时间序列,将被使用在查询中,同时和metric以及tag name/values(原文:But they will usually appear at the end of a series of branches in a branch that has one or more leaves but no child branches. Each leaf contains the TSUID for the timeseries to be used in a query as well as the metric and tag name/values
)。同时它也包含一个从规则集中解析出的display name,但是与任何metric,tag names或是tag values可能不是完全相同的事物。
理想状况下,一个时间序列点在一棵树中仅仅出现一次【注:应该是作为叶子仅仅出现一次】。但是如果对于一个时间序列的TSMeta对象或者是一个metric或是tag的UIDMeta被修改了,它可能会再次被处理并且作为一个叶子加入树中。这种情况经常出现在:对于metric,tag name或者是tag value,一棵树有一个custom rule,TSMeta 被处理,然后一个用户添加一个custom field和规则集匹配(原文:This can happen particularly in situations where a tree has a custom rule on the metric, tag name or tag value where the TSMeta has been processed then a user adds a custom field that matches the rule set
)。在这种情况下,推荐做法是开启严格匹配(strict matching
)功能,在树中,从而时间序列将不会再出现,除非custom data被再次添加。
Rules
每个树是动态构建的于一系列由用户定义的规则集合。一个规则集必须包含至少一个规则,并且通常将不止一个。每个规则集有多层去决定规则处理的顺序(原文:Each set has multiple levels that determine the order of rule processing
)。位于层次0上的规则被第一个处理,然后是位于层次1上的,依次类推,直到所有的规则被应用于给定的时间序列。规则集中的每层可能有多个规则去处理场景,metrics 和 tags 可能没有提前计划好,或者一些任意的数据可能会被阻塞。【这句话实在翻译不好,还请各位读者指点一二】(原文:Each level in the rule set may have multiple rules to handle situations where metrics and tags may not have been planned out ahead of time or some arbitrary data may have snuck in
)。如果多个规则被存储在同一层次中,第一个匹配成功的将会被应用,其它的则会被忽略。这些规则集合同样由order字段排序,所以一个order为0的规则将会第一个被处理,然后是order 为1的字段,以此类推。在日志中,当使用了测试点,规则IDs经常以如下格式给出:[<treeId>:<level>:<order>:<type>]
,例如:[1:0:1:0]
表示的规则就是:tree的ID为1,在0层,order为1,并且type为metric。
Rule Types
每个rule作用于时间序列数据的单个组件。通常可用的types包括:
Rules Config
单个rule能够处理正则表达式,分隔符或者不处理。如果一个rule同时定义一个正则表达式和一个分隔符,仅仅只有正则表达式会被处理,分隔符则会被忽略。
对规则的所有更改都经过验证,以确认适当的字段被填满,以便规则能够处理数据。下列的字段对于每个rule type必须被填充。
Type field customField
Metric
Metric_Custom X X
TagK X
TagK_Custom X X
TagV_Custom X X
Display Formatter【显示格式器】
偶尔地,从一个tag或者是metric中抽取数据可能不是非常具有描述性。例如:一个应用可能输出一个带有如下tag 对的时间序列:“port=80”
,或者 “port=443”
。标准的rule匹配tagk 值“port”,我们应该得到两个分支,分别是80,443(原文:With a standard rule that matched on the tagk value "port", we would have two branches with the names "80" and "443"
)。 没有经验的同学可能不知道这些数字意味着什么。因此,用户能够基于formatter定义一个标志,为了显示有用的信息,这将改变分支的输出。例如,我们能够声明一个格式器:tag_name:value
,从而上述的分支将会显示port:80
,以及port:443
。
标志是大小写敏感的并且每个formatter必须且只能出现一次。它们还必须像下表中那样定义:
Regex Rules
在某些情况下,为了使用分组,你可能想抽取一个metric,tag,或者一个custom value的一个组件。例如:如果你在多个数据中心有具有完全限定的域名的电脑,这个完全限定域名包含DC的名字,但不是所有的metrics都包含DC标志,为了分组,你能使用一个正则表达式去提取DC。(原文:For example, if you have computers in mutiple data centers with fully qualified domain names that incorporate the name of the DC, but not all metrics include a DC tag, you could use a regex to extract the DC for grouping
)
正则表达式的rule参数必须被设置成一个有效的规则的表达式,这个表达式包含一个或者更多个抽取操作符,例如:括号。如果正则表达式与提供的值匹配,抽取的值将会被用于构建成分支或者叶子。如果正则表达式中提供了不止一个抽取,你能够使用regex_group_index
参数去选择使用什么抽取值。索引是从0开始增长,并且默认是0,所以如果你想选择第二次抽取的输出,你应该设置这个索引为1。如果正则表达式不能匹配值或者抽取失败,将会返回一个无效的字符串,规则将会被认为不匹配。
例如:如果我们有一个tagk:host,其tagv是web01.nyc.mysite.com
,我们将使用一个类似于.*.(.*)\..*\..*
的正则表达式去提取FQDN中的nyc部分,并且将在nyc数据中心中的所有servers分到nyc分支中(原文:For example, if we have a host tagk with a tagv of web01.nyc.mysite.com, we could use a regex similar to .*\.(.*)\..*\..* to extract the "nyc" portion of the FQDN and group all of the servers in the "nyc" data center under the "nyc" branch.
)。
Separator Rules【分隔符规则】
通常来说,对于一系列系统的metrics,均是带有分隔符的,如句号作为分割metric的组件。例如:"sys.cpu.0.user"
。为了构建一个有用的tree,你能使用一个分隔符rule【separator rules
】,这将分割字符串基于字符序列并且从每个独立的值中创建一个分支或者是叶子。为之前的例子设置分隔符.
将会产生三个分支“sys","cpu","0"
以及一个叶子"user"
。
Order of Precedence
每个rule能够仅处理一个正则表达式,一个分隔符或者都不处理(原文:Each rule can only process a regex, a separator, or neither.
)。如果rule在它们的相对域里,有一个正则表达式和一个分隔符,只有正则表达式会被应用在时间序列中,而分隔符将会被忽略。如果正则表达式和分隔符均未被定义,那么rule的“field”将会被匹配,整个field的值将会被处理成一个分支或者是叶子。
Tree Building
第一,你必须在HBase中创建tsdb-tree表,如果你还没有这么做。如果你开启了tree processing并且表并不存在,那么TSDs将不会开始。
树能够以两种方式构建。配置tsd.core.tree.enable_processing
支持实时tree 创建(原文:The tsd.core.tree.enable_processing configuration setting enables real-time tree creation
)。无论什么时候用户创建、编辑一个新的TSMeta对象,TSMeta将会被传递给每个已配置的,或已开启的tree中(原文:Whenever a new TSMeta object is created or edited by a user, the TSMeta will be passed through every configured and enabled tree.
)。结果的分支将会被记录在存储中。如果一个碰撞发生或者TSUID不能匹配任何的rule,一个警告将会被记录在日志中,如果tree 选项被配置了,可能被记录在存储中。
可选的,你能够通过CLI uid 工具周期地同步所有的TSMeta对象。这将扫描tsdb-uid表并且传递每个发现的TSMeta对象到已配置和开启的trees中。详见uid。
Note
为了实时构建tree,你需要开启tsd.core.meta.enable_tracking设置,同时当一个时间序列点被接收的时候,TSMeta对象能够被创建。
创建一棵树的通常处理步骤如下:
- 1.通过HTTP API创建一棵新树
- 2.通过HTTP API给树分配一个或更多个规则
- 3.通过HTTP API,测试规则,使用一些TSMeta对象
- 4.在验证分支将会正确出现时,设置tree的开启标志为true
- 5.通过treesync 子命令运行uid工具,为了去同步树中存在的TSMeta对象
Note
当你创建一棵新树的时候,它(这个它指的是什么?)默认将会关闭,所以TSMeta对象将不会被应用rule set。所以你有时间去配置rule set并且测试它,为了去验证tree将会按照你想的那样构建。
Rule Processing Order
一棵树通常将会超过一个rule在顺序中,对于结果树是非常有用的。正如上述note所述,rules被组织成levels 和 orders。一个TSMeta被传递rule set 开始层次为0并且order为0。处理过程以递增的顺序在一个层次上处理规则(原文:Processing proceeds through the rules on a level in increasing order)。在第一个rule在一层上的成功匹配TSMeta 数据,处理调到下一层。这意味着:在一层上的rules是有效的(原文: This means that rules on a level are effectively or
ed )如果层次0有规则在order 0,1,2,3。并且TSMeta 匹配rule在order 为1 时,那么rule 将会跳过order 2/3。
Strict Matching
所有的TSMeta对象被应用在每棵树中。如果你仅仅想单个,巨大的tree去组织所有的openTSDB时间序列,这并不是个问题。但是如果你想创建许多树为了信息具体的子集,你可能想排除某些时间序列节点。那么strictMatch标志在一棵树中帮助去过滤时间序列点属于一棵树但是并非另外一棵树中的数据。strict Matching开启,一个时间序列点必须匹配一个规则在每个层次上(有一个或者更多个rules)顺序处于规则集中,为了它能够在树中包含。如果meta不能匹配任何任何层次上的rules,它将被记录为一个不匹配的条目并且没有叶子会产生。
默认情况下,严格匹配是关闭的,所以在一棵树中会尽量多的去匹配时间序列。如果你改变这个设置在一棵树中,你可能想删除存在的分支并且运行一个re-sync。
Collisions
由于规则集的灵活性以及metric、tag name/value 命名的广泛,不可避免的是:两种不同的TSMeta条目将会尝试创建相同叶子在同一棵树中。每个分支能够仅仅有一个叶子 一个给出的display name。例如,如果一个分支有一个叶子命名为user,并且其uid是010101,但是tree却尝试添加一个新的叶子,也叫user,但是却有着uid为020202,新的叶子不会被加入到tree中。相反,一个碰撞条目将会被记录下来:tsuid 020202与既存的叶子结点010101发生了碰撞。HTTP API能够被用于查询碰撞名单为了查看是否特殊的TSUID没有出现在树中由于一个碰撞。
Not Matched
当严格匹配开启时,一个TSMeta必须匹配一个rule在规则集的每层为了被加入到tree中。如果一个或者多个层不能匹配,TSUID将不会添加。类似碰撞,一个没有匹配的条目将会被记录,对于每个TSUID,不能被写入到tree中。失败的条目将会包含TSUID和一个简短的信息:关于哪个规则以及层次不能匹配。
Examples
假设我们的TSD存储有如下的时间序列:
···
注意对于这个例子来说,我们不会使用任何的custom value rules,所以我们不需要展示TSMeta 对象,但是假设这些值构建成了一个TSMeta对象。同样,为了便于说明,每个TSUIDs被截短为1个字节。
现在,让我们建树,不开启strictMatching,以及以下规则:
···
这个规则集合的目的在于将我们的数据按照数据中心->主机->metric的顺序排序。我们的公司可能有成千上万台服务器遍布全球,所以毫无意义的是将其展示在同一棵树的分支中,相反,我们想根据数据中心分组,并且让用户根据需要再做进一步处理。
在我们示例数据中,我们有一些老的时间序列,它们没有dc 这个tag name。然而,host 这个tag的确有一个完全限定域名,同时数据中心名字被嵌套。因此,我们规则集中的第一层有两个规则。第一个将会寻找一个dc tag,如果它找到了,它将会使用tag 的值并且第二个就会被跳过。如果dc tag不存在,那么第二个rule 将会扫描host 标签值并且尝试提取数据中心名从FQDN中。第二层有一个规则并且它是用于分组
主机tag的值,从而所有属于其tag的metirc都能够在分支下面显示。最后一层有metric rule:包含一个分隔符去进一步分组时间序列通过数据包含值。因为我们有多个CPU并且应用metric,所有均按照点号分割,在这上面添加一个分隔符是有意义的。