数据存储优化
数据热点之salting
Opentsdb官方文档
先解释下OpenTSDB用到的几个术语,方便大家理解。metric:指标,比如在系统监控中cpumem的利用率、系统Load、IO等都是指标。timestamp:时间戳tag:标签,其实表示在哪个维度。比如在cpu在某个机器上的数据,就可以把机器ip作为tag打进去。在OpenTSDB里tag是个k-v,比如ip=19160.1就可以做为一个tag。注意OpenTSDB最多只能打8个tag。value:我们要存的时序数据的值。
OpenTSDB对hbase的读做很多的封装,方便实现更复杂切灵活的查询功能,我们来看下读接口的查询参数就能一窥究竟。
OpenTSDB其他特性
在2版本,opentsdb进一步对数据存储做了优化,把每个Row里的3600列合并成了一列,存储格式如下。
我们原始数据可能长这样,一个小时总共有3600行的数据。Opentsdb会将一个小时的数据合并到一个Rowkey里,相应的Rowkey里的timestamp也会归一化到哪个小时的时间戳。如下。
Rowkey的优化
其实OpenTSDB不是一个通用的数据存储服务,看名字就知道,它主要针对于时序数据。什么是时序数据,股票的变化趋势、温度的变化趋势、系统某个指标的变化趋势……其实都是时序数据,就是每个时间点上纪录一条数据。关于数据的存储,我们最熟悉的就是mysql了,但是想想看,每5分钟存储一个点,一天288个点,一年就10万+,这还是单个维度,往往在实际应用中维度会非常多,比如股票交易所,成千上万支股票,每天所有股票数据就可能超过百万条,如果还得支持历史数据查询,mysql是远远扛不住的,必然要考虑分布式存储,最好的选择就是Hbase了,事实上业内基本上也是这么做的。。
Filtering过滤Grouping分组Downsampling数据缩放Interpolation数据改写Aggregation聚合RateConversion计算斜率Functions执行函数Expressions表达式运算
如果我们只通过原始的Hbase接口去存时间序列,我们可能会设计出这样的Rowkey。metric|timestamp|tagK1:tagVtagK2:tagV..如果我们每秒存储一个数据点,每天就有86400个数据点,在hbase里就意味着86400行的数据,不仅浪费存储空间,而且还查起来慢,所以OpenTSDB做了数据压缩上的优化,多行一列转一行多列,一行多列转一行一列。数据开始写入时其实OpenTSDB还是一行一个数据点,如果用户开启了数据压缩的选项,OpenTSDB会在一个小时数据写完或者查询某个小时数据时对其做多行转一行的数据压缩,压缩后那些独立的点数据就会被删除以节省存储空间。
多行一列转一行多列
这是一行非常特殊的数据,Rowkey为0x00,里面保存了metric、tagk、tagv已分配uid的最大值,每次新分配一个uid就会对其中相应的值增加这里用了hbase的原子操作,就是为了确保uid不重复。
这个时候OpenTSDB就应运而生。首先它做了数据存储的优化,可以大幅度提升数据查询的效率和减少存储空间的使用。其次它基于hbase做了常用时序数据查询的API,比如数据的聚合、过滤等。另外它也针对数据热度倾斜做了优化。接下来挨个说下它分别是怎么做的。
当每列不是存贮数值而是一个object的时候,Qualifier是3-5个bytes,然后第一个字节的开头16进制必须是0x0用来标识存的是个对象而不是时序点,后面两个byte用来表示这个object是哪个时间点,如果是毫秒级数据,后面就是4个bytes。里面的value是UTF-8编码的json串。
OpenTSDB在从底层hbase拿到数据后数据处理流程如下。
了解Hbase的人都知道,它可以通过加机器的水平扩展迅速增加读写能力,非常适合存储海量的数据,但是它并不是关系数据库,无法进行类似mysql那种select、join等操作。取而代之的只有非常简单的Get和Scan两种数据查询方式。这里不讨论Hbase的相关细节,总之,你可以通过Get获取到hbase里的一行数据,通过Scan来查询其中RowKey在某个范围里的一批数据。如此简单的查询方式虽然让hbase变得简单易用,但也限制了它的使用场景。针对时序数据,只有get和scan远远满足不了你的需求。
通过这样转换后一个Rowkey就能查到一个小时的数据,一天24个Rowkey也就够了。在hbase中Qualifier是个两个Bytes的整数,value是4-8bytes的数值。
有一点需要注意的,不要突发奇想去开关这个功能,这个集群要么从始至终开这个功能要么不开。否则你中途开关,会导致操作前的数据无法查询。另外还有一点,开启salting会影响查询性能,比如你想扫某个metric下所有数据,这个时候就得遍历所有的bucket。
opentsdb在构建Rowkey的时候并不是直接用原始值的,而是将metric、timesta、tagk、tagv分别用了一个3字节的uid做了替代,可以减少Rowkey的存储空间。所以这里需要有个表来做uid和真实值之间的转换,这个表就是tsdb-uid表。UID表中有两个family,分别是uid-name的映射,name-uid的映射。另外还有一行特殊的数据,就是uid已分配情况记录表。
一行多列转一行一列
用hbase存过数据的人可能会知道,大多数时候数据分布是存在严重倾斜的,可能20%的metric会聚集80%的数据,甚至更严重。因为hbase把数据按照Rowkey增序存储在不同的机器上,所以这种数据切斜很可能会导致hbase集群某些机器读写压力非常大,但别的机器却没什么压力。为了解决数据热点的问题,OpenTSDB在2版本增加了对数据做分桶的能力。其实就是对每行数据的tags做hash,然后把hash值拼接到Rowkey前面,这样就可以使同metric产生的rowkey分散开,减少数据热点发生的可能性。可以通过以下两个参数开启salting功能。
tsd.storage.salt.width #桶宽度(说实话这个参数的意义我没理解)
tsd.storage.salt.buckets #设置最大多少个bucket
数据查询
UID分配行
文章为作者独立观点,不代表股票配资公司观点