opentsdb探索之路——部分设计与实现

基于opentsdb-2.4.0版本,本篇开启opentsdb探索之路(主要涉及读写特性以及一些其他细节),下一篇将开启opentsdb优化之路——性能优化思路与建议(总结当前痛点问题、优化思路和解决方案,同时也欢迎朋友提出更好的思路与方案)。
注意:阅读本篇文章应该要对HBase有最基本的认识,比如rowkey、region、store、 ColumnFamily、ColumnQualifier等概念以及HBase逻辑结构、物理存储结构有大致的认知。

opentsdb 概览(overview)

opentsdb总体架构图

上图取自官方。其中的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]<metric_uid><timestamp><tagk1><tagv1>[...<tagkN><tagvN>]

暂不考虑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:tagv
metric01   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()

// if the meta cache plugin is instantiated then tracking goes through it if (meta_cache != null) { meta_cache.increment(tsuid); } else { // tsd.core.meta.enable_tsuid_tracking if (config.enable_tsuid_tracking()) { // tsd.core.meta.enable_realtime_ts if (config.enable_realtime_ts()) { // tsd.core.meta.enable_tsuid_incrementing if (config.enable_tsuid_incrementing()) { TSMeta.incrementAndGetCounter(TSDB.this, tsuid); } else { TSMeta.storeIfNecessary(TSDB.this, tsuid); } } else { // 写入rowkey为tsuid,value为1的记录 final PutRequest tracking = new PutRequest(meta_table, tsuid, TSMeta.FAMILY(), TSMeta.COUNTER_QUALIFIER(), Bytes.fromLong(1)); client.put(tracking); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfffd.html