时序处理数据库,更快的存储和聚合
OpenTSDB 是为存储时序数据而设计的,它基于 HBase 存储数据,充分发挥了 HBase 的分布式列存储特性,支持数百万每秒的读写,支持千万数目的 Metric,它的特点就是容易扩展,具有灵活的 Tag 机制。其主要用途,就是做监控系统,譬如收集大规模集群( 包括网络设备、操作系统、应用程序 )的 监控数据 并进行存储和聚合查询,在目前的 IoT 方面具有很大的应用价值。
Hbase Schema
OpenTSDB 是基于 Hbase 存储系统的,主要利用了 Hbase 数据自动排序 以及 可靠的分布式特性。 OpenTSDB 在安装启动时,默认在 Hbase 里面创建 四张 表。分别为:
- tsdb: 存储数据点表,也就是存储实际的时序数据,绝大部分的数据是存在这个表中;
- tsdb-uid: 存储 name 和 uid 的映射关系,也就是给字符串的键、值映射成数值,通常包括 metric、tagk、tagv 等字符串映射。
- tsdb-meta: 元数据表,这个只有通过配置文件开启才会存储数据的,默认不开启。如果开启了存储的就是你传过来的完整的 json 格式数据,没有经过解析的,这就是元数据。
- tsdb-tree: 树形表,这个也是只有开启配置文件选项才能使用,开启后可以由自己创建和管理自己的树形 metric 结构,需要自己设计管理的。
下面主要说明下 tsdb 和 tsdb-uid 的表结构。
UID Table Schema
这里我们主要分析下 OpenTSDB 存储 UID 的表 – tsdb-uid。
首先,看下 tsdb-uid 的表结构。其存储的是字符串到 UID 的映射关系。
我们可以通过 hbase shell
通过 Hbase 访问数据库入口查看小 tsdb-uid 的表结构:
1 | list 'tsdb-uid' |
上面结果我只列出了少量信息,主要看 NAME 就可以了,表示该表的 Column Family, 分别为 name 列族和 id 列族。那具体怎么将字符串映射为 UID ? 这里就需要通过实际的例子来说明。
我们先上传一个数据,格式内容如下,可以通过 Postman 进行上传测试,OpenTSDB 默认上传地址为 http://ip:4242/api/put?details
,后缀 details 是为了查看上传反馈。
1 | [ |
上传成功后返回:
1 | { |
我们先看下我们上传的数据格式,需要进行映射的字符串是对应 metric
, tagkey
, tagvalue
的,这里 tag
有两组,所以要映射的有 5 个字符串,分别为: sys.test.metric, hostname, jiyiren, area, shanghai.
那我们就再用 Hbase Shell 查看表内容:
1 | scan 'tsdb-uid' |
从中我们可以看到,数据总是成对出现的,包括 UID 映射字符串 和 字符串映射 UID。上面 5 组是 UID 映射成字符串,下面 5 组是字符串映射为 UID. 前面已经看过 tsdb-uid 表有两个列族,而其中的 name
列族对应的就是 UID 映射成字符串,而 id
列族对应字符串映射为 UID, 这正是这两个列族的作用,这样对于正反查找速度都是极快的。
此外,对于 UID 映射字符串的,每行数据,也就是 rowkey
相同的,至少包含三个列,分别是 metrics
, tagk
, tagv
. 我们可以通过前三行结果看出。
到这里我们知道了 UID 与字符串间是怎么映射以便于查询的,但是 UID 到底是怎么生成的呢?
实际上大家在前面操作 scan 'tsdb-uid'
的时候,结果会列出额外三行以 \x00
开头的数据:
1 | ROW COLUMN+CELL |
实际上 UID 是用 3 bytes 表示的非负整型数,并且是自增的,而自增的就要依赖于上一次插入的最新 ID
值,这三行就是分别保存 metrics, tagk, tagv 插入的最新数据的 UID,这样下次插入新的数据只要在对应的值上加 1 就能得到其对应的 UID 了。
Data Table Schema
我们再看看 OpenTSDB 的实际存储时序数据的表 – tsdb。
既然 UID 与字符串的映射关系搞定了,那么真实的时序数据存储就好理解了。tsdb 保存了所有的时序数据,其 rowkey
就是由各个字段对应的 UID 组成的。
先查看下 tsdb 数据库结果:
1 | scan 'tsdb' |
结果值太长了,可以分开看,先看列族里的数据【18.7.10 更正为】:
1 | column=t:I\x00, timestamp=1528521000278, value=\x0A |
其中 value=\x0A
而 0X0A
化为十进制就是 10, 正好是我们前面上传的 metric
的值。
再看看 rowkey:
1 | \x00\x00\x01[\x1Fa`\x00\x00\x01\x00\x00\x01\x00\x00\x02\x00\x00\x02 |
rowkey
是 OpenTSDB
设计的独特之处,其构成规则为:
1 | [salt]<metric_uid><timestamp><tagk1><tagv1>[...<tagkN><tagvN>] |
salt
是为了更好的分布式,
我们的上面添加的 metric
为,其中 tagk
会自动按字母排序,所以 area 排在前面:
1 | # 字符串对应 |
除了 timestamp 和上面结果完全对应,而 timestamp 则是按小时存储的,也就是取 3600 的整数倍的 timestamp 作为当前时间戳。计算方法 timestamp - timestamp % 3600.
这样,我们应该对 OpenTSDB 的 UID 以及 Rowkey 的生成和存储结构都基本了解了。
相关链接
- http://opentsdb.net/docs/build/html/index.html
- http://liubin.org/blog/2016/03/05/tsdb-opentsdb/
- https://www.jianshu.com/p/0bafd0168647
- http://www.nosqlnotes.com/technotes/opentsdb-tabledesign/
img.godjiyi.cn