OpenTSDB原理精解

参考:http://opentsdb.net/docs/build/html/index.htmlhttps://blog.csdn.net/yulin_hu/article/details/81805858http://www.binospace.com/index.php/opentsdb-design-road/

OpenTSDB是一个架构在Hbase系统之上的实时监控信息收集和展示平台。

1、它用hbase存储所有的时序(无须采样)来构建一个分布式、可伸缩的时间序列数据库。

2、它支持秒级数据采集所有metrics,支持永久存储,可以做容量规划,并很容易的接入到现有的报警系统里。

3、OpenTSDB可以从大规模的集群(包括集群中的网络设备、操作系统、应用程序)中获取相应的metrics并进行存储、索引以及服务

从而使得这些数据更容易让人理解,如web化,图形化等。

对于运维工程师而言,OpenTSDB可以获取基础设施和服务的实时状态信息,展示集群的各种软硬件错误,性能变化以及性能瓶颈。

对于管理者而言,OpenTSDB可以衡量系统的SLA,理解复杂系统间的相互作用,展示资源消耗情况。集群的整体作业情况,可以用以辅助预算和集群资源协调。

对于开发者而言,OpenTSDB可以展示集群的主要性能瓶颈,经常出现的错误,从而可以着力重点解决重要问题。

1.基础概念

我们以一个监控系统的背景来介绍这些概念。比如我们需要记录的数据是一个集群里面各个服务器磁盘的使用率。

Metric:监控项,这里就是磁盘的使用率。

Tags:就是一些标签,在OpenTSDB里面,Tags由tagk和tagv组成,即tagk=takv(键值对)。标签是用来描述Metric的,也可以说是用来区分各个Metric,比如一个集群里有多台电脑,我们需要用主机名来区分,这里tagk为hostname,tagv即为各个主机号。Tags可以有多个。

Value:一个value就是一个metric对应的实际值,比如磁盘使用率50%。

Timestamp:时间戳(时间序列),指明时间点,用以表示这条数据是什么时候的。

DataPoint:即某个Metric在某个时间点的数值。DataPoint包括以下部分:Metric、Tags、Value、Timestamp,在OpenTSDB里面存储的就是多个DataPoint。

2.存储设计之道

OpenTSDB的设计思路主要有两个,一是将一个维度一段时间的数据聚在一起,二是使用定长字典值压缩数据,主要依赖两张HBase表实现,tsdb和tsdb-uid。

表 tsdb包含一个列族“t”,tsdb-uid包含两个列族”id”和“name“,在HBase中列族名字越短,所占用的字节数也就越短,越有利于存储;tsdb是我们存放数据的表,tsdb-uid是 tsdb 的辅助表,用于查找uid对应的指标或者指标对应的uid。

UID全称为Unique Identifier。这些UID信息被保存在OpenTSDB的元数据表中,默认表名为”tsdb-uid”。

OpenTSDB分配UID时遵循如下规则:

1)metrics、tagKey和tagValue的UID分别独立分配;

2)每个metrics名称(tagKey/tagValue)的UID值都是唯一。不存在不同的metrics(tagKey/tagValue)使用相同的UID,也不存在同一个metrics(tagKey/tagValue)使用多个不同的UID;

3)UID值的范围是0x000000到0xFFFFFF,即默认地,UID被编码为3字节,metrics(或tagKey、tagValue)最多只能存在16777216个不同的值。你也可以修改源代码改为4字节。

假设我们要存放以下的数据:

metric:proc.loadavg.1m

timestamp:1234567890

value:0.42

tags:host=web42,pool=static

2.1.表tsdb

tsdb表

RowKey的设计:

RowKey的设计是metric+timestamp+tagk+tagv。但是OpenTSDB并不是简单地将这些数据组合起来,OpenTSDB为了节省存储空间,将每个部分都做了映射。

这里面除了timestamp,每一项都在uid表中有一个映射。默认是每一个值都映射到一个3字节的整数,就是24位,2^23是800W+,但是我们可以自己配置字节数,比如我们可以用四个字节,那就是几十亿的数据量。

这里映射关系为:proc.loadavg.1m-->052、host-->001、web42-->028、pool-->047、static-->001。

这样Rowkey的设计还有一个好处,就是各个项的长度是固定的,对于原来的每一个metric,tag key,tag value的长度都是不固定的,这不利于通过字节偏移量来直接定位它们。(否则需要使用特定的分隔符来对各个项进行分割,而且为了避免输入信息中可能存在特定的分隔符导致解析出错,还要对所有输入信息的分割符进行转义处理)

Column的设计:

列的设计依赖于timestamp,如果我们的将一行数据表示为一个小时的,那么我们将timestamp除以小时(3600),就可以得到这条数据是哪一个小时,余数作为列名。那么1234567890得到的是1234566000,余数1890。那么1234566000会放在rowKey中,1890会作为列名。表示这条数据是在这个小时里面的第1890秒。

这里的设计是充分利用了HBase列可以动态扩充的特点。我们并不知道这一个小时会有多少条数据,但是同一时间段来的数据余数肯定不一样,我们就可以不断地添加列。

2.2.表tsdb-uid

tsdb-uid这里保存的就是一些metric,tagk,tagv的一些映射关系。结构如下图

tsdb-uid表

有两个列族,其中列族name将uid映射到一个字符串,这里的uid就是我们在tsdb表中用作rowkey的;列族id则是将字符串映射到uid。这两个列族的数据其实是相互对应的,两者存储的值和rowKey是互反的。

这里再说明一下这张表的作用:前面说过这是一张辅助表,他就是存储一些映射关系。但是由于这张表的存在,可以节省空间。我们的metric,tag会在数据中重复出现多次,但是它在tsdb-uid这张表中只会存一次,并且会有一个唯一的uid与之对应。这样在tsdb中再存储这个数据的时候,我们反复存储的就不是这个值而是这个uid,很多情况下这个uid的长度远远小于这个值的长度,这样就在一定程度上节省了很多空间。

同时在前面提到这里的uid默认是3字节整数,2^23是8百多万,但是这个数据并不是说我们只能存储8百多万条数,而是8百多万种数据(这个数据量是指metric或是tag的种类),前一段提过相同的metric和tag对应的是同一个uid,而2^23是说可以有这个量的uid存在,而这些uid可以对应多少条数据取决于数据中数据项的重复程度。

2.3.优势

它在海量数据的压力下,仍然保证了存储的效率,那么它背后有什么值得借鉴的地方呢?

1)使用AsyncHbase而非HBase自带的HTable。使用线程安全、非阻塞、异步、多线程并发的HBase API,在高并发和高吞吐时,可以获得更好的效果。建议在使用AsyncHBase​时,在CPU core有保证的前提下,可以设置16或者24。

2)采用固定长度的Rowkey,让Rowkey包含尽可能多的检索信息。这一点的话,OpenTSDB存储的数据要包含大量的metrics和tag信息,这些信息的长度是变长的,因此,在实现上设置了一个表格uid-tsdb存储这些信息,作为一个全局唯一的编号,并把编号与TimeStamp合并作为Rowkey。

3)每一行要存储尽可能多的信息,这一点在OpenTSDB被发挥到了极致。例如,把某个时间段的分散采集的数据合并在一起,按照一个Row来提交给hbase。这种方案,会减少整个表格Rowkey的个数,从而提高检索Row的速度,但是该方法并没有节省存储空间。在这个基础上,OpenTSDB又推动了一步,让一个column记录多条内容,从而降低存储空间的浪费。

4)按照时间的Boundary来存储,仍采用无状态的存储方案,从而提供系统的容错能力。

附录:OpenTSDB中一个KeyValue的存储结构如下:


KeyValue存储结构

3.总体架构


OpenTSDB的数据流图

Servers:就是服务器了,每一个需要获取metrics的Servers都需要设置一个Collector用来收集时间序列数据;上面的C就是指Collector,可以理解为OpenTSDB的agent,通过Collector收集数据,推送数据;

TSD:TSD是hbase对外通信的daemon程序,没有master/slave之分,也没有共享状态,Collector可以通过TSD简单的RPC协议推送监控数据;另外TSD还提供了一个web UI页面供数据查询;另外也可以通过脚本查询监控数据,对监控数据做报警;

HBase:TSD收到监控数据后,是通过AsyncHbase这个库来将数据写入到HBase;AsyncHbase是完全异步、非阻塞、线程安全的Hbase客户端,使用更少的线程、锁以及内存,可以提供更高的吞吐量,特别对于大量的写操作。

推荐阅读更多精彩内容