智慧出行HBase-一劳永逸解决Hbase数据热点问题:1.java实现HBase建表并且预分区

    技术2022-07-10  124

    1.hbase热点问题是什么?

    数据倾斜到一个节点,其余节点空转

    2.出现haase热点的原因是什么?

    出现haase热点情况一定是这两个原因:

    rowkey设计太low没有做预分区

    3.设计hbase经验

    rowkey设计原则:唯一,如果不唯一数据就会被覆盖,2散列的,防止出现hbase热点问题,3,字典 rowkey长度设计2的整数倍16,32,64,最长不超过64位.只有这样才可以使用高速缓存,如果不是这些,不能被64整除,就只能使用主机内存,那是比较慢的(最快是寄存器,其次是高速缓存,再往后是内存,磁盘) 列族只设计1个,如果数据量特别大,也就做2个列族,不要在多了.列族最好使用一个字母表示"M",最多使用两个字母"MM"定义其名称

    4.为什么row可以设计要使用字典序列去排序的?

    为了便利scan操作

    5.HBase创建表实现预分区

    1.命令行创建HBase的demo01表:

    create 'demo01',{NAME=>'M'},SPLITS=>['0','1','2','3'] //列族为一个字母,最大不要超过两个,要使用splits指定预分区

    2.通过java代码实现建表并且预分区

    hbase给我们提供了两个方法可以做预分区:

    RegionSplitter.HexStringSplit:适合于当前rowkey是16进制字符串(7E8A9S777A)RegionSplitter.UnformSplit:适合于rowkey经过hash,MD5处理过后的

    0000| 0001| 0002| 0003|…0007| 竖杠是ASCII中最大的值,因为我们rowkey是字典排序,增加竖杠就就一点会在这个范围之内

    2.1工具类:实现预分区前缀二进制数组的生成

    创建一个指定分区数量的接口 package com.cartravel.hbase; /** * 创建一个指定分区数量的接口 */ public interface SplitKeyCalulaor { public byte[][] getSplitKeys(int regionNum); }

    byte[][]二进制数组

    实现过程中要运用到lang3这个包下的leftPad()左填充构建前缀

    StringUtils.leftPad(null, *, *) = null StringUtils.leftPad("", 3, 'z') = "zzz" StringUtils.leftPad("bat", 3, 'z') = "bat" StringUtils.leftPad("bat", 5, 'z') = "zzbat" StringUtils.leftPad("bat", 1, 'z') = "bat" StringUtils.leftPad("bat", -1, 'z') = "bat" @param str:要给那个的字符串左边进行拼接,可以为null @param size:拼接后总字符个数 @param padChar:拼接的字符 实现预分区前缀的生成 package com.cartravel.hbase; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hbase.util.Bytes; import java.util.Iterator; import java.util.TreeSet; /** *工具类实现的是:生成预分区前缀 */ public class SparkKeyBuilder implements SplitKeyCalulaor { //############## 实现上述接口生成预分区的前缀(返回的是二进制数组) ################### public byte[][] getSplitKeys(int regionNum) { //1.构建前缀 String[] keys = new String[regionNum]; for(int i=0;i<regionNum;i++){ String pre = StringUtils.leftPad(String.valueOf(i), 4, "0") + "|"; keys[i] = pre; } //2.构建一个返回值数组 byte[][] splitKeys = new byte[keys.length][]; //使用一个有序集合封装前缀 TreeSet<byte[]> row = new TreeSet<byte[]>(); for (int i = 0; i < keys.length; i++) { row.add(Bytes.toBytes(keys[i])); } //迭代TreeSet,把值赋值给splitKeys Iterator<byte[]> iterator = row.iterator(); int i =0; while(iterator.hasNext()){ byte[] tempRow = iterator.next(); iterator.remove(); splitKeys[i] = tempRow; i++; } row.clear(); row=null; return splitKeys; } }

    2.2 通过预分区前缀的二进制数组和表描述器实现创建生成hbase表

    package com.cartravel.hbase import com.cartravel.loggings.Logging import org.apache.hadoop.hbase.{HColumnDescriptor, HTableDescriptor, TableName} import org.apache.hadoop.hbase.client.{Connection, TableDescriptorBuilder} import org.apache.hadoop.hbase.io.compress.Compression.Algorithm import org.apache.hadoop.hbase.regionserver.BloomType object hbaseOperation extends Logging { private def createTableBySelfBuilt(conn:Connection,tableName:TableName,regionNum:Int,column:Array[String]): Unit ={ this.synchronized{ val admin = conn.getAdmin try{ if (admin.tableExists(tableName)){ //要想创建表,需要一个表的描述器 val tbDesc: HTableDescriptor = new HTableDescriptor(tableName) if (column != null){ column.foreach(c =>{ val hcd = new HColumnDescriptor(c.getBytes()) hcd.setBlockCacheEnabled(false) hcd.setMaxVersions(1) hcd.setBloomFilterType(BloomType.ROW) //布隆过滤器 hcd.setCompressionType(Algorithm.SNAPPY) //指定压缩方式 //将列表示器添加到表描述器里面 tbDesc.addFamily(hcd) }) } val sparkKeyBuilder = new SparkKeyBuilder() val splitKeys = sparkKeyBuilder.getSplitKeys(regionNum) //建表 admin.createTable(tbDesc,splitKeys)//拥有预分区的hbase表就创建完毕了 } }catch { case e:Exception=> //邮件报警 } } } }
    Processed: 0.014, SQL: 9