服务端优化
什么导致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协处理器受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);
}
}
}