基于opentsdb-2.4.0版本,本篇开启opentsdb探索之路(主要涉及读写特性以及一些其他细节),下一篇将开启opentsdb优化之路——性能优化思路与建议(总结当前痛点问题、优化思路和解决方案,同时也欢迎朋友提出更好的思路与方案)。
注意:阅读本篇文章应该要对HBase有最基本的认识,比如rowkey、region、store、 ColumnFamily、ColumnQualifier等概念以及HBase逻辑结构、物理存储结构有大致的认知。
上图取自官方。其中的TSD(对应实际进程名是TSDMain)就是opentsdb组件。每个实例TSD都是独立的。没有master,没有共享状态(shared state),因此实际生产部署可能会通过nginx+Consul运行多个TSD实例以实现负载均衡。
Each TSD uses the open source database HBase or hosted Google Bigtable service to store and retrieve time-series data
我们大多应该还是用HBase作为数据存储。
安装部署一文中提到过在HBase中创建表结构,这里先简单介绍一下这4张表(table),随着探究的深入会对tsdb和tsdb-uid这两张表有更深刻的认识,至于tsdb-meta、tsdb-tree两张表不是这里讨论的重点,简单了解一下即可。相关文档:
tsdb: opentsdb全部的时序数据都存在这张表中,该表只有一个名为"t"的列族(ColumnFamily)。所以这张表的数据非常大,大多情况下读写性能瓶颈也就与这张表密切相关,进而优化也可能与它相关。
rowkey的设计为an optional salt, the metric UID, a base timestamp and the UID for tagk/v pairs,即[可选的salt位+metric的UID+小时级别的时间戳+依次有序的tagk、tagv组成的UID键值对],如下:
暂不考虑salt位,关于加salt下面有章节单独拿出来看它的设计与实现。来看一个不加salt且含有两个tag的时序数据的rowkey组成:
00000150E22700000001000001000002000004 '----''------''----''----''----''----' metric time tagk tagv tagk tagv至于rowkey为什么要这样设计以及具体实现,后面详细介绍,这里先有个基本认知。
tsdb-uid: 为了减少rowkey的长度,opentsdb会将metric、tagk、tagv都映射成UID,映射是双向的,比如说既可以根据tagk找到对应的UID,也可以根据UID直接找到相应的tagk。而这些映射关系就记录在tsdb-uid表中。该表有两个ColumnFamily,分别是name和id,另外这两个ColumnFamily下都有三列,分别是metric、tagk、tagv。如下图所示:
RowKey id:metric id:tagk id:tagv name:metric name:tagk name:tagvmetric01 0x01
metric02 0x02
tagk01 0x01
tagv01 0x01
tagv02 0x02
0x01 metric01
0x01 tagk01
0x01 tagv01
0x02 metric02
0x02 tagv02
从上面可以看出,metric、tagk、tagv三种类型的UID映射互不干扰,这也就使得0x01这个UID在不同类型中有着不同的含义。后面会从源码角度讲一下uid大致的分配。
tsdb-meta: 在完成时序数据的写入之后,会根据当前opentsdb实例的配置决定是否为相关时序记录元数据信息。看一下opentsdb.conf配置文件中tsd.core.meta.enable_tsuid_tracking配置项即可。
tsd.core.meta.enable_tsuid_tracking(默认false): 如果开启该选项,每次写入一个DataPoint(时序数据)的同时还会向tsdb-meta表中写入rowkey为该时序数据的tsuid(下面会讲到它,即完整的rowkey除去salt和timestamp后的数据), value为1的记录。这样,每个点就对应两次HBase的写入,一定程度上加大了HBase集群的压力。相关代码见TSDB#storeIntoDB()#WriteCB#call()