Opentsdb On Hbase 设计 region 预分区
如何查看region读写是否分布均匀
方法一: 通过hbase webui页面
a) 查看opentsdb.conf 中 指定的存储数据点的HBase表名 由此得知表名是tsdb
b) 通过hbase ui 查看数据分布 hbase版本不同查看方式会有出路
方法二:通过查看hdfs目录结构
c) 查看hdfs目录下文件大小是否平衡
如何设计hbase rowkey
- Opentsdb源码中 rowkey生成
插入数据:http://localhost:4242/api/put?details {“metric”: “E0021DJMH0.D5.C00A18E01.60”,“timestamp”: 1577808000,“value”: 1.2,“tags”: {“cn”: “CAA”}} |
---|
Row_key的长度 跟tagk,v的个数有很大的关系,在存入hbase前,opentsdb对于rowkey一直采用byte[] 类型进行操作,而这个byte[] 的长度是:
加盐字节数 + 提取metric默认字节数 + 提取时间戳编码的字节数 + tagk个数*tagk所占的字节数 + tagv个数 * tagv所占的字节数 = 1+3+4+1 x 3+1 x 3 = 14个字节
除了tagk,takv的个数是不确定因素,其他的字节个数都是与配置文件相关的
Slat = 加盐字节数 = 默认是0 = tsd.storage.salt.width
Metric提取字节数 = 默认是3 = tsd.storage.uid.width.metric
tagk所占的字节数 = 默认是3 = tsd.storage.uid.width.tagk
tagv所占的字节数 = 默认是3 = tsd.storage.uid.width.tagv
时间戳进行编码的字节数 = 默认是4
当前插入数据的rowkey长度为 14个字节
加盐: {8,0,0,1,94,11,112,-128,0,0,1,0,0,1}
未加盐(字节原本格式): {0,0,1,94,11,112,-128,0,0,1,0,0,1}
由此可见: 当opentsdb设置加盐时 是对字节第一位进行加盐,但是又有什么规律呢
通过上图可得知 首字节加盐的取值范围就为 设置的bucket的个数进行求模,首字节的获取范围不会大于bucket的个数
基于tsdb加盐时划分预分区区间
1、 opentsdb.conf 说明
# 可以在rowkey的前面添加salt,使得每行数据分散在hbase不同的region上,以便写入性能更好
tsd.storage.salt.width = 1
# 将数据分配至hbase每个rgion中
tsd.storage.salt.buckets = 40
2、Region预分区设计代码(修改for循环次数与bucket个数一致)
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 40; i++) {
String result = Utils.toTsdbUid(i);
list.add(String.format("'%s'",result));
}
System.out.println(list.toString().replaceAll(" ",""));
}
public static String toTsdbUid(long value) {
int i;
char[] bytes;
String result = Long.toHexString(value);
StringBuilder sb = new StringBuilder();
if (result.length() % 2 == 0) {
bytes = result.toCharArray();
} else {
result = "0" + result;
bytes = result.toCharArray();
}
for (i = 0; i < bytes.length; i++) {
if (i % 2 == 0)
sb.append("\\x");
sb.append(String.valueOf(bytes[i]).toUpperCase());
}
return sb.toString();
}
基于tsdb不加盐时划分预分区间
-
参考文档 https://blog.csdn.net/qq_31806205/article/details/70918059
-
参数说明:
- 输入分区区间范围
- 这个参数参考项目的metric个数,假设估摸项目metric个数为5000个,则区间设置为5000 (最好稍微大一点)
- 输入region个数
- 此参数就是你指定生成region的个数
- 输入分区区间范围
-
Rowkey一键生成代码
package com.sec.sedt.bi;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
/**
* Created by fanzhongyu on 2017/9/6.
* 针对uid为3字节,可以这么映射
*/
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
/**
* Created by fanzhongyu on 2017/9/6.
* 针对uid为3字节,可以这么映射
*/
class Utils {
public static String toTsdbUid(long value){
int i;
char[] bytes;
String result = Long.toHexString(value);
StringBuilder sb = new StringBuilder();
if(result.length()%2==0){
bytes = result.toCharArray();
}else{
result = "0"+result;
bytes = result.toCharArray();
}
for(i=0;i<bytes.length;i++){
if(i%2==0)
sb.append("\\x");
sb.append(String.valueOf(bytes[i]).toUpperCase());
}
return sb.toString();
}
public static String formatTsdbUid(String tsdbUid){
String reuslt = null;
StringBuilder sb = new StringBuilder();
if(tsdbUid.length() == 4){
sb.append("\\x00\\x00");
sb.append(tsdbUid);
}else if(tsdbUid.length() == 8){
sb.append("\\x00");
sb.append(tsdbUid);
}else{
sb.append(tsdbUid);
}
return sb.toString();
}
public static String makeSplitString(List<String> stringList){
StringBuilder sb =new StringBuilder();
sb.append("[");
for(int j=0;j<stringList.size()-1;j++){
sb.append("'").append(stringList.get(j)).append("',");
}
sb.append("'").append(stringList.get(stringList.size()-1)).append("'").append("]");
return sb.toString();
}
public static String hbaseSplitString(int range,int regionCount){
String result=null;
List<String> stringList=new LinkedList<String>();
int i = range/regionCount;
for(int j=1;j<regionCount;j++){
stringList.add(formatTsdbUid(toTsdbUid(i)));
i=i+range/regionCount;
}
result = makeSplitString(stringList);
return result;
}
}
public class Presplit {
public static void main(String[] args){
int range;
int regionCount;
Scanner reader = new Scanner(System.in);
System.out.print("输入分区区间范围:");
range = reader.nextInt();
System.out.println("输入region个数");
regionCount = reader.nextInt();
System.out.println(Utils.hbaseSplitString(range,regionCount));
// System.out.println("a".toUpperCase());
}
}
将生成好的分区区间复制进tsdb 建表语句中(create_table.sh)
在创建tsdb 表的语句最后添加 slits=> 刚才idea生成的区间数组
删除原有tsdb表(或采用datax对tsdb到tsdb进行转移)
hbase-shell>disable 'tsdb'
drop 'tsdb'
disable 'tsdb-uid'
drop 'tsdb-uid'
disable 'tsdb-meta'
drop 'tsdb-meta'
disable 'tsdb-tree'
drop 'tsdb-tree'
Env COMPRESSION=NONE HBASE_HOME=/opt/apps/hbase2.0.6 ../src/create_table.sh
写入数据后的效果图
数据分布均匀了,如有不解请发送邮件至zy619300@163.com 邮箱,都是看源码debug出来的结果,大家如有不解,可通过源码进行dubug进行排查
结论
Hbase在没有设置预分区时,会从一个region开始对数据进行插入,当一个region数据达到region物理的最大阈值时,会分裂出第二个region,在region只有一个的时候,配置open的tsd.storage.salt.buckets参数不会起到任何作用,当region分割后,这个参数才会起到作用。所以我们设计加盐时 bucket的个数最好和 预分区region的个数一致,这样数据就可以合理的分配到每个hbase region上了,在没有设置预分区时,数据最好不加盐,因为加盐后产生的作用微乎其微,需要等到数据量起来后,加盐的效果才有可能会出现