HBase优化策略及协处理器

服务端优化

什么导致HBase性能下降

Jvm内存分配与GC回收策略

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

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

HBase数据存储过程

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

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

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

常见服务端配置优化

Jvm设置与GC设置

hbase-site.xml部分属性配置

属性简介建议

hbase.regionserver.handler.countrpc请求的线程数量,默认值是10

hbase.hregion.max.filesize当region的大小大于设定值后hbase就会开始split建议手动split

hbase.hregion.majorcompactionmajor 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.sizeregionserver的block cache的内存大小限制在偏向读的业务中可调大该值

hbase.hregion.memstore.flush.sizememstore超过该值将被flush

hbase.hregion.memstore.block.multiplier如果memstore的内存大小超过flush.size*multiplier,会阻塞该memstore的写操作建议设置成5,设置太大会有内存溢出的风险

常用优化策略

预先分区

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

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

Rowkey优化

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

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

Column优化

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

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

读写优化

写优化策略

同步批量提交(默认)

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

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

读优化策略

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

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

表结构设计问题

HBase协处理器

协处理器简介

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);

    }

  }

}

    原文作者:Alukar
    原文地址: https://www.jianshu.com/p/3e074bc865c2
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞