Opentsdb插入数据导致RegionServer写入请求分布不均匀
1. 配置文件
首先先普及一下Opentsdb的配置文件是怎么获取的,根据官方介绍,在输入Opentsdb启动命令如果你没有指定 –config的路径的话,会自动搜索以下路径来寻找配置文件:
./opentsdb.conf
/etc/opentsdb.conf
/etc/opentsdb/opentsdb.conf
/opt/opentsdb/opentsdb.conf
2.Opentsdb在Hbase中是如何存储的
个人感觉,如果想排查问题,必须要了解Opentsdb中的表结构是如何的,这里就简单的介绍下两个表结构,tsdb和tsdb-uid:
图一
1.\x00那一行是系统生成和维护的,value的值代表Metrics和tagk以及tagv的数量有多少个。
2.在Opentsdb中,Metrics ,tagk,tagv都会被映射为一个三字节的唯一标识,我们称为uid,它们组合在一起就会形成一个序列的UID以及TSUID,在Opentsdb中每一个Metric,tagk,tagv都是从0开始计数,每当你要创造新的Metric,tagk,tagv,对应的uid就会加一,这里是顺序增长的,但是,这也会引入一个问题我们将在下面继续讨论
图二
通过图一和图二两个图片我们可以看出tsdb存放的数据的映射是双向的,我们既可以通过Metric,tagk,tagv找到它们对应的uid,也可以通过uid找到相对应的Metric,tagk,tagv。讲到这里,可能许多读者对表结构还是不够清楚,所以这里列出一个表格来更加方便大家直观的看出‘tsdb-uid’的表结构:
图三
表结构就如图三所示因为真实数据点位太长了,我就捏造了一些假的数据填入到表中,不过意思大同小异,我们可以看出,tagk和tagv单独占一行,这也是问什么Opentsdb鼓励我们复用tagk和tagv,因为当我们建立不同的tagk和tagv越来越多时,Hbase中的行也就越多,那扫描‘tsdb-uid’的效率也会相应地降低。
接下来我们看一下实际存储数据的tsdb表是如何设计的:
图四
一个Hbase表设计的好不好,很大一部分看Rowkey设计的合不合理,同理,我们来看一下Opentsdb是如何在Hbase中设计表结构的。
rowkey的设计:
指标UID(指标+标签的某个组合)+ 数据生成时间(取整点时间)+标签1-Key的UID+标签1-Vlaue的UID+…+标签N-Key的UID+标签N-Vlaue的UID
UID(Metrics name,3字节):\x00\x00\x01
数据生成时间(4字节):X\xED\xEC\x90 = 01011000 11101101 11101100 10010000 = 1491987600 相当于北京时间 2017/4/12 17:0:0 大家可能会有疑问,你这个时间,为什么刚刚好是整点,不能这么巧吧,哈哈,当然不能这么巧~不要忘记,Opentsdb是按照小时把数据聚合的,那具体到一个小时的某些时间,我们应该如何看呢?
我们看到列t后面有个值是\x0B\x1B将它转化为10进制为2834,我们用1491987600+2834 = 1491990434 北京时间为 2017/4/12 17:47:14 这样我们不就能具体到某个小时内的某个时间点了呢,这样做法的好处是大大减少了行数,节约了存储空间,提升了检索效率。
tagk(3个字节):\x00\x00\x01 对照我们上面的映射表,其实就是quality
tagv(3字节):\x00\x00\x01对照我们上面的映射表,其实就是我们的值192。
这俩组合在一起,不正好是quality = 192嘛?
接下来那个value其实就是这个点位的具体数值,16进制的,转化的过程是一样的,就不一一列举了。
3遇到的问题
接下来就遇到了问题,先贴图:
图5
偶然间我发现了Hbase的写请求分布的很不均匀,如上图所示,大部分的写请求集中在第一张图片上,而且大部分的写请求,集中在某个region上面,这样会导致的问题是写请求集中在某个RS上面,不能充分发挥集群的特性,也就是写操作时,只有其中几台RS发挥了功能,而其他的都在闲置状态,并且你的写操作,都集中在某些region上,这会导致region里面的memstore很快就写满了,写满了之后就会flush操作,导致频繁的flush操作,如果你频繁写入的region暂时不能发挥功能时,可能会导致Opentsdb写入数据失败等等一些问题。
4解决问题
我们现在的问题就是新写入的数据都集中在某个RegionServer上的某个Region上面,首先想到的是Hbase中,如果数据都在一个Region上面,那么它的rowkey一定是连续的,我马上去看了下tsdb-uid表中的点位和uid的映射,发现了如下情况
图6
我们发现,我们刚插入的点位的uid都是连续的,还记得我们第二节提到的tsdb表中的Rowkey是以uids开头的嘛,这也就是说,我们新写入的数据都是递增连续的,也就导致了我们新写入的数据都集中在某个RegionServer上的某个Region上面,造成了写入数据的热点。
然后我查看了Opentsdb配置文件,tsd.core.auto_create_metrics,有一个这样的点位,我们标识的是true,也就是自动生成uid和点位的映射,再加上文章开头所说,uid的创建都是自增的,所以造成了热点数据,那我想,有没有可能使Rowkey前面的uid随机一下呢,我去Opentsdb官网上配置信息页面(http://opentsdb.net/docs/build/html/user_guide/configuration.html),看了一哈,确实找到了可以让uids随机生成一下的配置,tsd.core.uid.random_metrics (2.2),这个配置选项,只支持2.2以后的版本,2.1是不行的。。。。我把这个配置选项改成了True,问题得到了初步的缓解。
但是!!!!虽然问题得到了解决,我感觉还引入了其他的问题,我们随机生成uids,uids的总共个数是2的24次方(3字节),也就是说随机生成uid可能导致uid碰撞,然后导致写入数据失败!我看网上有人说,可以吧uid增长到8字节,减少碰撞概率,但是如果增长到8字节,Rowkey就会变得很长,官方其实也不是很推荐,所以,这个配置选项看个人或者我们可以采取预先创建uid来平衡分布,但是我们具体也不可能完全知道有多少个点位,所以,问题是找到了,解决办法也找到了,如何解决的话呢,还是要看具体的业务。希望大家结合自己的业务来选择最合适的调优手段,而我最后抛出的问题,接下来我将会一一实践,然后再下一篇文章中给出大家合理的解释,如果大家遇到了我这种问题,不妨试一下tsd.core.uid.random_metrics (2.2)这个配置选项,前提是你的Opentsdb版本是2.2以后的哦~
因为这篇文章是个人原创,可能还有许多不足或者错误的地方,希望广大喜欢这方面的人多多提提建议,也可以加我的扣扣,具体的联系方式可以私信我,欢迎大家的批评和指正,么么哒~