Opentsdb 2.3 Writing data(一)

来源:互联网 发布:红帽linux 10天就重启 编辑:程序博客网 时间:2024/06/07 06:55

写数据

你也许迫不及待的想将数据放入到TSDB当中,但是为了能够充分发挥OpenTSDB的性能和灵活性,你可能需要暂时停一下并且认真的思考一下你得命名规则。当你已经深思熟虑的思考过你得命名规则之后,你可以通过Telnet或者HTTP API来插入你的数据,或者使用扩展的第三方工具来使用OpenTSDB,例如’tcollector’。

命名规则

许多点位设计者经常使用一些单一的名字来定义他们的时间序列。举个例子,系统管理员会使用RRD风格来命名他们的时间序列如 webserver01.sys.cpu.0.user。这个名字传达给我们的意思是记录某个用户在服务器Webserver01上占用cpu 0的时间的一段时间序列。这对于如果您希望稍后在该Web服务器上检索该CPU核心的用户时间,这是一个伟大的设计。

但是如果Web服务器有64个核心数并且你想要从64个CPU核心当中获取一些平均cpu使用时间,该怎么做?一些系统允许你去指定一些未知数例如webserver01.sys.cpu.*.user 这样就可以读取64个文件然后聚合结果集。或者,你也可以记录一个叫webserver01.sys.cpu.user.all 的新的时间序列来实现同样的聚合效果,但是如果这样你就必须写’64+1’个不同的时间序列。假设你有一千台服务器,你想要获取整个服务的所有服务器的cpu平均使用时间,你该怎么做呢?你当然可以巧妙地运用通配符来进行查询像.sys.cpu..user 然后系统就会读取64000个文件,然后聚合结果并最终返回数据。或者你可以设置一个预处理过程然后将结果写入到webservers.sys.cpu.user.all 这个点位中。(这里感觉像抛砖引玉,先说出传统的弊端,然后接下来开始说OpenTSDB如何好了,开始我们的表演,巴拉巴拉巴拉。。。。)

但是,OpenTSDB由于引入了’tags(标签)’这个概念使得这件事情变得不同起来。每个时间序列仍然有’指标’名字,但是它更加通用,标签被许多时间序列所共享。反而,我们使用key/value键值对来区分指标名字(Metrics name),引入Key/value键值对带来的好处呢就是可以通过快速的聚合然后达到更加灵活查询的目的。

注意

每一个时间序列在OpenTSDB中至少要有一个标签

我们继续用之前的那个例子,依旧是webserver01.sys.cpu.0.user这个点位。在OpenTSDB当中,也可以表示成sys.cpu.user {host=webserver01, cpu=0} 这种形式,如果我们想要去查某个单独的核心,我们可以 sum:sys.cpu.user{host=webserver01,cpu=42} 这样巧妙地查询,如果我们还想获取所有的核心使用量呢,我们只需要将cpu这个标签(tag)去掉然后告诉TSDB sum:sys.cpu.user{host=webserver01} 这样做,这样TSDB就会聚合64个核心使用量然后返回聚合结果。如果我们还想要聚合1000台服务呢,我们只需要sum:sys.cpu.user 这样简单的请求,就可以达到使用者想要得到的数据。由于TSDB下层采用Hbase进行存储,TSDB这种数据结构会使所有的sys.cpu.user 这个点位的时间序列一个挨一个的存储在一起,这样的好处就是我们对这个点位时间序列做聚合操作时非常的迅速和有效率。(因为Hbase是按照rowkey字典顺序存储的,因为同一个点位的uid是相同的,所以同一个点位的不同时间序列其实是储存在一起的,这是Hbase的特性,而TSDB巧妙地设计Rowkey,使同一个点位的数据存储在一起。如果现在还不明白可以先记住这个妙处,看到后面自然而然就会懂了~)。OpenTSDB致力于使这些聚合查询尽可能的快,从而让用户站在一个更高的层次去使用这个产品,接下来我们将会一一详细的讲解这些概念。

聚合

虽然引入标签(tag)是非常灵活的一个事情,但是如果使用者在查询是使用不当,也会引起不小的麻烦,因此你在使用tag查询时,需要经过一定的思考。接下来举一个查询的例子:sum:sys.cpu.user{host=webserver01} (64 CPU cores),我们在webserver01记录下来64个独立的时间序列,当我们发出查询请求时,标签为host=webserver01 的点位 sys.cpu.user 所有CPU cores 时间序列都会被检索,然后返回一条时间序列。我们假设在时间戳为1356998400得到的聚合结果为50.现在我们移动到另一台Opentsdb的服务上并且预处理把这个结果当成新的点位写入TSDB,sys.cpu.user host=webserver01 值为50写入到OpenTSDB当中。紧接着,如果我们执行如上相同的查询命令,我们同样会在时间戳为1356998400得到一个具体的数值,但是结果会有什么不同呢?OpenTSDB不仅仅会聚合你原本存在的64个点位,还会把你新插入的那个预处理的点位也算入聚合操作当中(64+1),这样就会得到了数值100。。。(很明显,我们需要明确,精确地知道我们需要查询地是什么,不能随意的使用这些聚合功能,否则的出的结果可能和你预期的有出入)如下图:

这里写图片描述

在使用OpenTSDB的时候,如果查询的时候没有加上任何的标签(tags),TSDB将会自动聚合该点位的所有时间序列。如果一个或者多个标签(tag)被指定,将会按照你指定的标签所聚合,而不管其他未指定的标签。举个例子,当我们使用这条命令查询时 sum:sys.cpu.user{host=webserver01} ,会聚合host标签以外的所有标签,会包含 sys.cpu.user host=webserver01,cpu=0,sys.cpu.user host=webserver01,cpu=0,manufacturer=Intel, sys.cpu.user host=webserver01,foo=bar and sys.cpu.user host=webserver01,cpu=0,datacenter=lax,department=ops. (很明显,这个查询四不像,与我们的预期有一些出入),这个简单的例子告诫我们:仔细认真的设计你的数据结构。

时间序列的基数

任何命名模式的一个关键方面是考虑您的时间序列的基数(其实就是数据量),基数被定义为一个集合中的项。在OpenTSDB中,这意味着许多项关联而成一个点位(metrics),这些项其实就是独一无二的点位名字(metrics name),以及标签名字和标签值所组成的键值对。基数很重要,下面就列出了两个比较重要的原因:

UID

每个点位的点位名称,标签名字和标签值得UID都是有限的(TSDB会把点位名称,标签名字和标签值都映射为UID)。默认的情况下,每个类型具有大约1600万的UID可以使用。举个例子,你运行了一个非常受欢迎的网站服务并且你想通过标签来追踪客户的ip地址,就像这个样子 web.app.hits clientip=38.26.34.10 ,你将会很快超过TSDB的UID使用上限(1600万,3字节),因为ipv4会有超过40亿的不用ip地址。此外,这种方法可能会导致创建一个非常稀疏的时间序列,有可能38.26.34.10这个ip地址的客户只是偶尔的使用你的服务应用,或者下次再也不使用你的应用了。

然而,UID的限制通常是不会达到上限的。因为点位的名字,标签名字以及标签值所分配的UID都是相互隔离的。并且如果某个标签值是数字而且已经分配了UID,并且其它的标签名字想指向同样的值,这个UID是可以复用的。举个很简单的例子,如果你已经分配了一个标签值给数字2,我们可以存储时间序列的标签键值对 cpu=2, interface=2, hdd=2 and fan=2,这里面我们会分配几个UID呢?有些人可能会说是8个,错了,TSDB没那么的蠢,其实是5个,分别是一个标签值 2 的UID和4个 标签名字((cpu, interface, hdd and fan)。这样不就节省了很大的空间,不用去存储重复的UID了吗?

如果你认为UID数量限制影响到了你使用OpenTSDB。首先,你要确认一下,你是否真的需要那么多的UID数量。如果就像我们上面所举的例子那样点位名字是 web.app.hits,你可能只关心服务的总点击数,而很少深入到特定的ip地址。在这种情况下,你可能希望将ip地址存储为注释,这样你仍然可以保证你的TSDB处于低基数并且从中获益。如果你需要的话,你可以借助外部脚本来搜索特定ip的结果。(注意 : 对注解进行查询将在未来的TSDB版本中实现。)

如果你十分确信你需要的UID数量超过1600万,你可以在OpneTSD中将UID的存储空间从原来的3字节增加到8字节(UID的数量也就从对应的2的24次方增加到2的64次方),不过更改这项配置需要修改源代码中的值,从新编译,将您的更改代码部署到相应的TSD节点上去,并且在未来的补丁和发布中维护这项定制。

警告

有可能您的业务场景确实需要这么多的UID。如果你选择扩大UID的存储容量,你必须使用全新的数据和全新的UID表(tsdb表和tsdb-uid表),任何以3字节UID方式写入的数据都将会与8字节UID数据不兼容。因此,确保所有的TSD服务运行相同的修改代码并且你之前存在TSDB的数据(3字节UID)可以借助外部的工具导出到另外一个系统上,具体详见TSDB.java文件来修改此值。

查询速度

数据基数同样是影响查询速度的一个很重要的因素,因此你需要考虑到你经常执行的查询并且优化您的命名规则。OpenTSDB会将每个点位的每小时数据放在一行中(Hbase)。如果我们只有一个主机,这个主机只有一个内核,并且每天我们每秒向sys.cpu.user host=webserver01,cpu=0 这个时间序列写入数据,那么说一天我们就会产生24行数据并且会写入86400个点位(24*60*60)。然后,我们一台主机如果有8个内核,那一天我们就会有192行数据和691200个点位写入。其实这看起来数据还不算多,我们可以轻松的通过像 start=1d-ago&m=avg:sys.cpu.user{host=webserver01} 这样的查询指令来获取所有内核的总数或者平均数。这个查询指令将**遍历**192行,并将结果聚合为单个时间序列。

但是如果我们有20000台主机呢,每个主机有8个内核呢?现在我们每天将会产生380万行数据和17.28亿数据,这将导致OpenTSDB中数据量有太大的基数(显然这是不好的),如果我们查询主机webserver01内核的平均数,我们将会从380行中挑选出192行。(如果是OpenTSDB2.2,你可以使用cpu=*标签来指明你要聚合的值然后Hbase会启用过滤器来帮助跳过那些不必要的行)

这样设计模式的好处是您的数据有非常高的粒度。例如你在以内核数和主机数为标签作为这个点位存储的基础的时候,你可以轻松的编写一个查询语句,查询所有主机的所有内核的平均数 start=1d-ago&m=avg:sys.cpu.user 。然后,对于这个特定的查询将需要更长的时间,因为需要筛选更多的行。这在所有的数据库中都很常见,并不是OpenTSDB的问题。

下面是一些处理大数据量时的一些办法:

预聚合 - 在上面的例子当中sys.cpu.user 这个点位中,管理者通常只关心每台机器的平均内核使用情况而不是每个内核的使用情况。虽然数据采集器可以通过上面的标记模式为每个核心发送单独的值。但是数据采集器同样也可以发送一个每台机器的平均内核使用情况的点位 sys.cpu.user.avg host=webserver01 (这个的意思就是,我们如果要算出一天的所有机器所有内核的平均使用情况,我们完全可以先以每台机器为单位,做一次预聚合,聚合每天机器的每个内核平均使用情况,然后再来一次聚合,算出每个机器的平均使用情况,这样效率远远大于一次聚合)。因为这一次的预聚合,你现在拥有了一个全新的时间序列,每天每台机器只有24行,如果还是2万台机器,我们只有4万8千台数据来进行筛选。这样你再进行聚合操作响应就会非常的迅速,而且你仍然可以进行细粒度的查询,比如某台机器的某个内核。

选择合适的命名规则 - 如果你只关心特定主机的某些点位,而不需要去跨主机去聚合,又要怎么办呢?如果你没有跨主机去聚合的需求呢,你可以将主机名放到点位名字中去。我们之前的例子点位就会变成 sys.cpu.user.websvr01 cpu=0 。针对这个模式的查询就会非常的快,因为这么设计点位,每天只会产生192行数据。但是这么设计的代价是,如果要以主机进行聚合,你必须执行多个查询,并且在OpenTSDB之外进行聚合(未来版本的OpenTSDB将包括这项功能)。

命名总结

当您设计你的点位的数据结构时,请记住以下几条建议 :

· 保持命名规范的一直,尽量减少重复。经常使用相同的点位名字,标签名字和标签值
· 每个点位使用相同的标签名字和标签值。例如,杜绝这种行为:
my.metric host=foo and my.metric datacenter=lga。
· 考虑到你经常要查询的业务,并且根据自身业务进行数据格式的优化。
· 考虑到你查询时,是否需要进行深入的查询。
· 每个点位不要使用太多的标签(tags),使标签的数量保持在相对较小的数量级上,一般4到5个足够(OpenTSDB的最大标签数为8个标签)。

接下来还会有许多内容,我也将会一一为大家呈现,如果我的语句不通或者观点错误的话呢,欢迎大家的批评指正,谢谢大家。

原创粉丝点击