HBase优化策略及协处理器

服务端优化

  1. 什么导致HBase性能下降

    • Jvm内存分配与GC回收策略

    • HBase运行机制相关的配置分配不合理

    • 表结构设计及用户使用方式不合理

  2. HBase数据存储过程

    • HBase写入时当memstore达到一定大小会flush到磁盘保存成HFile,当HFile小文件太多回执行compact操作进行合并(compact就是将很多小文件合并成一个大文件的过程。compact分为minor compaction和major compaction)

    • 当Region的大小达到某一阈值之后,回执行split操作

当HBase做compact和split操作时需要优化

  1. 常见服务端配置优化

    • Jvm设置与GC设置

    • hbase-site.xml部分属性配置

    属性 简介 建议
    hbase.regionserver.handler.count rpc请求的线程数量,默认值是10
    hbase.hregion.max.filesize 当region的大小大于设定值后hbase就会开始split 建议手动split
    hbase.hregion.majorcompaction major compaction的执行周期 将值设置成0,在业务低峰手动执行majorcompaction
    hbase.hstore.compaction.min 一个store里的storefile总数超过该值,会触发默认的合并操作 默认3
    hbase.hstore.compaction.max 一次合并最多合并多少个storeFile
    hbase.hstore.blockingStoreFiles 一个region中的Store(CoulmnFamily)内有超过多少个storeFile时,则block所有的写请求进行compaction
    hfile.block.cache.size regionserver的block cache的内存大小限制 在偏向读的业务中可调大该值
    hbase.hregion.memstore.flush.size memstore超过该值将被flush
    hbase.hregion.memstore.block.multiplier 如果memstore的内存大小超过flush.size*multiplier,会阻塞该memstore的写操作 建议设置成5,设置太大会有内存溢出的风险

常用优化策略

  1. 预先分区

    • HBase默认创建表的时候会自动创建一个Region分区

    • 创建表时预先创建一些空的Region,并指定Rowkey的存储范围。这样可以减少Split操作,减少IO操作

  2. Rowkey优化

    • 利用HBase默认排序特点,将一起访问的数据放到一起

    • 防止热点问题,避免使用时序或者单调的递增递减等

  3. Column优化

    • 列族的名称和列的描述命令尽量简单

    • 同一张表的列族不要超过三个

读写优化

  1. 写优化策略

    • 同步批量提交(默认)

    • 异步批量提交 (会提升性能,但可能存在数据丢失,在一些业务中可以采用)

    • WAL优化,是否必须开启(默认开启),持久化等级

  2. 读优化策略

    • 客户端:Scan缓存设置,批量获取

    • 服务端:BlockCache配置是否合理,HFile是否过多(通过服务端的配置进行设置)

    • 表结构设计问题

HBase协处理器

  1. 协处理器简介

HBase协处理器受BigTable协处理器的启发,为用户提供类库和运行时环境,使得代码能够在HBase RegionServer和Master上处理(使用协处理器需要谨慎,可能会导致性能下降甚至数据丢失)

协处理分为系统协处理器 and 表协处理器

  • 系统协处理器:全局加载到RegionServer托管的所有表和Region上(是属于所有的表)

  • 表协处理器:用户可以指定一张表使用协处理器(只是针对一张表)

  • 观察者(Observer):类似于关系数据库的触发器

  • 终端(Endpoint):动态的终端有点像存储过程

2.实现一个ResionObserver类型的协处理器

引入pom:



  org.apache.hbase
  hbase-common
  1.2.4


  org.apache.hbase
  hbase-server
  1.2.4

一个简单demo

package com.imooc.bigdata.hbase.coprocessor.observer;

import java.awt.image.ImagingOpException;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.CoprocessorEnvironment;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Durability;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import org.apache.hadoop.hbase.util.Bytes;



/**
 * Created by xt on 18-6-18.
 */
public class RegionObserverTest extends BaseRegionObserver {

  private byte[] columnFamily = Bytes.toBytes("cf");
  private byte[] countCol = Bytes.toBytes("countCol");
  private byte[] unDeleteCol = Bytes.toBytes("unDeleteCol");
  private RegionCoprocessorEnvironment environment;

  //regionserver 打开region前执行
  @Override
  public void start(CoprocessorEnvironment e) throws IOException {
    environment = (RegionCoprocessorEnvironment) e;
  }

  //RegionServer关闭region前调用
  @Override
  public void stop(CoprocessorEnvironment e) throws IOException {

  }

  /**
   * 1. cf:countCol 进行累加操作。 每次插入的时候都要与之前的值进行相加
   */

  @Override
  public void prePut(ObserverContext e, Put put, WALEdit edit,
      Durability durability) throws IOException {
    if (put.has(columnFamily, countCol)) {
      //获取old countcol value
      Result rs = e.getEnvironment().getRegion().get(new Get(put.getRow()));
      int oldNum = 0;
      for (Cell cell : rs.rawCells()) {
        if (CellUtil.matchingColumn(cell, columnFamily, countCol)) {
          oldNum = Integer.valueOf(Bytes.toString(CellUtil.cloneValue(cell)));
        }
      }

      //获取new countcol value
      List cells = put.get(columnFamily, countCol);
      int newNum = 0;
      for (Cell cell : cells) {
        if (CellUtil.matchingColumn(cell, columnFamily, countCol)) {
          newNum = Integer.valueOf(Bytes.toString(CellUtil.cloneValue(cell)));
        }
      }

      //sum AND update Put实例
      put.addColumn(columnFamily, countCol, Bytes.toBytes(String.valueOf(oldNum + newNum)));
    }
  }

  /**
   * 2. 不能直接删除unDeleteCol    删除countCol的时候将unDeleteCol一同删除
   */
  @Override
  public void preDelete(ObserverContext e, Delete delete,
      WALEdit edit,
      Durability durability) throws IOException {
    //判断是否操作cf列族
    List cells = delete.getFamilyCellMap().get(columnFamily);
    if (cells == null || cells.size() == 0) {
      return;
    }

    boolean deleteFlag = false;
    for (Cell cell : cells) {
      byte[] qualifier = CellUtil.cloneQualifier(cell);

      if (Arrays.equals(qualifier, unDeleteCol)) {
        throw new IOException("can not delete unDel column");
      }

      if (Arrays.equals(qualifier, countCol)) {
        deleteFlag = true;
      }
    }

    if (deleteFlag) {
      delete.addColumn(columnFamily, unDeleteCol);
    }
  }
}

3.实现一个Endpoint类型的协处理器

待续。。。。。。

你可能感兴趣的:(大数据环境)