OpenTSDB是一个分布式、可伸缩的时序数据库,支持高达每秒百万级的写入能力,支持毫秒级精度的数据存储,不需要降精度也可以永久保存数据。其优越的写性能和存储能力,得益于其底层依赖的HBase,HBase采用LSM树结构存储引擎加上分布式的架构,提供了优越的写入能力,底层依赖的完全水平扩展的HDFS提供了优越的存储能力。
在58,OpenTSDB目前主要用于数据平台监控系统中相关性能指标的存储查询,58智能监控系统中回归模型和分类模型原始明细数据存储查询,以及风铃监控预警系统数据的存储查询等。
OpenTSDB架构:
OpenTSDB主要由以下几部分组成:
(1)、Collector:负责从服务器上收集并通过telnet或http协议发送数据给TSD实例;
(2)、TSD实例:TSD是无状态的服务,负责接收Collector发送过来的数据,并对外提供数据查询服务,可以部署多个读写实例;
(3)、HBase集群:TSD接收到监控数据后,通过AsyncHbase将数据写入到HBase,AsyncHbase是完全异步、非阻塞、线程安全的HBase客户端,使用更少的线程、锁以及内存,可以提供更高的吞吐量,特别对于大量的写操作。
一、数据模型和存储优化
1、数据模型
OpenTSDB采用按指标建模的方式,一个datapoint包含以下几个部分:
(1)、metrics:指标名称,例如sys.cpu.user;
(2)、timestamp:秒级或毫秒级的Unix时间戳,代表该时间点的具体时间;
(3)、tags:一个或多个标签,也就是描述主体的不同的维度。Tag由TagKey和TagValue组成,TagKey就是维度,TagValue就是该维度的值;
(4)、该指标的值,目前只支持数值类型的值。
2、存储优化
OpenTSDB对HBase深度依赖,并且根据HBase底层存储结构的特性,做了很多巧妙的优化。
(1)、缩短rowkey的长度。OpenTSDB使用一个字典表tsdb-uid,将metrics和tag映射成3位整数的uid,存储指标数据时按uid存储,节省了大量存储空间,同时提高了查询效率。tsdb-uid表结构如下图所示:
(2)、减少keyvalue的数量。OpenTSDB实际存储数据的表为tsdb表,对于一小时内相同metrics和tags的datapoint,在tsdb表中OpenTSDB会合并成一行数据,并且更进一步,将所有列的数据会合并成一列,通过合并数据行和列减少了大量冗余key的存储,节省大量存储空间。tsdb表结构如下图所示:
(3)、并发写优化。为了避免热点问题,OpenTSDB支持在建数据存储表时,设置分桶,当某一个metrics下数据点很多时,可以将写压力分散到多个桶中,避免了写热点的产生。
二、案例分享
1、数据平台监控系统
监控系统架构:
数据平台监控系统监控的相关组件包括:Hadoop、HBase、Kafka、Flume和Zookeeper等。由于TSD实例是无状态的,我们部署了多个读写实例,对外通过域名进行数据读写分离,保证服务的高可用。采集的监控数据最终通过TSD实例存储到HBase集群,并基于HBase的RSGroup机制进行物理隔离。由于OpenTSDB的可视化能力较弱,并且没有报警功能,我们选择了Grafana作为监控系统的可视化和报警模块,Grafana支持多种数据源,OpenTSDB是支持的其中一个数据源,而且可视化效果非常好。
在我们的实际使用中,TSD单实例写QPS最高到达了10万 ,存储到HBase的监控数据量半年预估达到10T ,而支撑这一能力的后端HBase集群只用了5个节点,从而可以看出OpenTSDB强大的写性能。
Grafana中RegionServer活跃handler线程数的可视化截图:
2、58智能监控系统
58智能监控系统架构:
58智能监控系统中,OpenTSDB主要用于存储网络出口和业务的进出流量、集群和域名的访问量、宏观业务数据等的原始数据,并使用回归模型按天预测流量变化趋势,使用分类模型对实时流量做异常检测。
以下是流量预测效果图:
三、遇到的问题
在58智能监控系统和风铃监控预警系统使用OpenTSDB时,我们遇到了一个相同的问题:
某个metrics在同一时间段内写入了大量的datapoint,而且datapoint属于不同的标签组合,标签基数达到百万级,结果在查询这一时段该metrics数据时,响应非常缓慢,甚至将HBase的RegionServer所有handler线程阻塞,导致TSD实例最终不可用,严重影响了用户体验,但是发现查询其它时段该metrics的数据是没有问题的。
问题分析:
OpenTSDB数据存储表tsdb的rowkey结构:metrics timestamp tags,第一个是metrics,第二个是时间戳,第三个是tags,tags可能包含了多个标签名称和标签值,而这些标签之间是没有特定顺序的,比如tags可能取值为:{tk3=tv3,tk1=tv1,tk2=tv2,…},如下图中tsdb表rowkey部分。在进行查询时,查询条件包括该metrics,查询时间范围包括了有查询问题的时间段,查询标签取值为{tk2=tv2},如下图查询条件部分。在查询的时候由于无法知道数据写入时rowkey中标签之间的顺序,导致所有的OpenTSDB查询都只能进行前缀查询 filter,前缀查询字段包括metrics和timestamp,而标签匹配只能通过HBase的filter机制在RegionServer内存中进行过滤,如下图红色虚线部分。
而由于该查询时间段内metrics写入了大量标签的数据,标签量基数达到百万级,datapoint更是达到上亿,根据metrics ts的前缀过滤后,需要在RegionServer内存中filter的标签数据量达到百万级,进行百万级的数据量filter自然会导致查询响应变慢了。
问题解决:
(1)、建tsdb表时设置预分桶存储,提升并发查询性能,但是由于已经在生产环境中,无法重建该表,该方案舍弃。
(2)、删除多余数据。经过和业务沟通,多余入库的这部分数据暂时不需要统计分析,可以删除。考虑到在线删除数据肯定会对线上业务造成干扰,并且考虑到Hbase的LSM存储结构特性,最终采取在测试HBase集群删除数据然后合并到在线集群的方式解决了该问题。
以下是整个删除数据的操作流程,包括4个步骤:
①、在测试集群创建和在线集群相同表结构(包括分区信息)的tsdb表,修改hbase集群配置保证不要触发minor compact和major compact。使用跨集群拷贝工具distcp将在线集群tsdb表的hfile文件拷贝到测试集群,最后将hfile通过bulkload工具加载到测试tsdb表中。
②、在测试环境部署一个tsdb实例,并开启tsd.http.query.allow_delete=true,允许tsdb实例支持查询的同时删除对应记录,最后根据要删除的数据列表,执行查询删除操作。
③、通过distcp拷贝测试环境tsdb表的hfile到hbase在线集群,并执行bulkload操作导入正式的tsdb表。
④、对在线集群tsdb表执行major compact,删除问题数据,最终查询性能恢复正常。
更多思考:
如果后续还需要对这种删除的数据进行分析,其实我们可以转变思路,数据存储的时候将tag变成metrics来存储,这样就不用担心查询性能问题了。
四、总结
本文从OpenTSDB的整体架构,存储模型,存储优化,在58的使用情况和使用过程中遇到的问题等多个方面进行了详细描述。展望未来,我们希望OpenTSDB在数据安全,多租户方面得到进一步的优化和完善,这样可以将OpenTSDB打造成一个统一的平台,简化现有的部署流程,用户也可以更放心和更容易接入使用。