Opentsdb On Hbase 设计 region 预分区

Opentsdb On Hbase 设计 region 预分区

如何查看region读写是否分布均匀

方法一: 通过hbase webui页面

a) 查看opentsdb.conf 中 指定的存储数据点的HBase表名 由此得知表名是tsdb
image-20220214165845858

b) 通过hbase ui 查看数据分布 hbase版本不同查看方式会有出路

image-20220214165912568

image-20220214165917621

方法二:通过查看hdfs目录结构

c) 查看hdfs目录下文件大小是否平衡

image-20220214165922238

如何设计hbase rowkey

  • Opentsdb源码中 rowkey生成
插入数据:http://localhost:4242/api/put?details {“metric”: “E0021DJMH0.D5.C00A18E01.60”,“timestamp”: 1577808000,“value”: 1.2,“tags”: {“cn”: “CAA”}}
image-20220214165943391

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设置加盐时 是对字节第一位进行加盐,但是又有什么规律呢

image-20220215084621586

通过上图可得知 首字节加盐的取值范围就为 设置的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)

image-20220215085102092

image-20220215085108377

在创建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 

写入数据后的效果图

image-20220215085135152

数据分布均匀了,如有不解请发送邮件至zy619300@163.com 邮箱,都是看源码debug出来的结果,大家如有不解,可通过源码进行dubug进行排查

结论

​ Hbase在没有设置预分区时,会从一个region开始对数据进行插入,当一个region数据达到region物理的最大阈值时,会分裂出第二个region,在region只有一个的时候,配置open的tsd.storage.salt.buckets参数不会起到任何作用,当region分割后,这个参数才会起到作用。所以我们设计加盐时 bucket的个数最好和 预分区region的个数一致,这样数据就可以合理的分配到每个hbase region上了,在没有设置预分区时,数据最好不加盐,因为加盐后产生的作用微乎其微,需要等到数据量起来后,加盐的效果才有可能会出现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值