# HBASE应用及优化


<!--more-->


## 1.  Java客户端

示例代码: client包 https://gitee.com/ixinglan/hbase.git

## 2. 协处理器

### 2.1 概念

访问HBase的方式是使用scan或get获取数据，在获取到的数据上进行业务运算。但是在数据量非常大的时候，比如一个有上亿行及十万个列的数据集，再按常用的方式移动获取数据就会遇到性能问题。客户端也需要有强大的计算能力以及足够的内存来处理这么多的数据。

此时就可以考虑使用Coprocessor(协处理器)。将业务运算代码封装到Coprocessor中并在RegionServer上运行，即在数据实际存储位置执行，最后将运算结果返回到客户端。利用协处理器，用户可以编写运行在HBaseServer端的代码。

HbaseCoprocessor类似以下概念

- 触发器和存储过程：一个Observer Coprocessor有些类似于关系型数据库中的触发器，通过它我们可以在一些事件（如Get或是Scan）发生前后执行特定的代码。Endpoint Coprocessor则类似于关系型数据库中的存储过程，因为它允许我们在RegionServer上直接对它存储的数据进行运算，而非是在客户端完成运算。
- MapReduce：MapReduce的原则就是将运算移动到数据所处的节点。Coprocessor也是按照相同的原则去工作的。
- AOP：如果熟悉AOP的概念的话，可以将Coprocessor的执行过程视为在传递请求的过程中对请求进行了拦截，并执行了一些自定义代码。

### 2.2协处理器类型

- Observer

  协处理器与触发器(trigger)类似：在一些特定事件发生时回调函数（也被称作钩子函数，hook）被执行。这些事件包括一些用户产生的事件，也包括服务器端内部自动产生的事件。协处理器框架提供的接口如下

  - RegionObserver：用户可以用这种的处理器处理数据修改事件，它们与表的region联系紧密。
  - MasterObserver：可以被用作管理或DDL类型的操作，这些是集群级事件。
  - WALObserver：提供控制WAL的钩子函数

- Endpoint 

  这类协处理器类似传统数据库中的存储过程，客户端可以调用这些Endpoint协处理器在Regionserver中执行一段代码，并将RegionServer端执行结果返回给客户端进一步处理。

- Endpoint常见用途

  聚合操作

  > 假设需要找出一张表中的最大数据，即max聚合操作，普通做法就是必须进行全表扫描，然后Client代码内遍历扫描结果，并执行求最大值的操作。这种方式存在的弊端是无法利用底层集群的并发运算能力，把所有计算都集中到Client端执行,效率低下。

  使用EndpointCoprocessor，用户可以将求最大值的代码部署到HBase RegionServer端，HBase会利用集群中多个节点的优势来并发执行求最大值的操作。也就是在每个Region范围内执行求最大值的代码，将每个Region的最大值在RegionServer端计算出，仅仅将该max值返回给Client。在Client进一步将多个Region的最大值汇总进一步找到全局的最大值。

### 2.3 Observer案例

需求: 通过协处理器Observer实现Hbase当中t1表插入数据,指定的另一张表t2也需要插入相对应的数据。

见示例代码: MyProcessor

添加hbase-server依赖-->打成jar包,上传hdfs-->挂载协处理器-->验证协处理器(-->卸载协处理器)

## 3. RowKey设计

- 长度原则

  rowkey是一个二进制码流，可以是任意字符串，最大长度64kb，实际应用中一般为10-100bytes，以byte[]形式保存，一般设计成定长。

  > 建议越短越好，不要超过16个字节
  >
  > 设计过长会降低memstore内存的利用率和HFile存储数据的效率。

- 散列原则

  建议将rowkey的高位作为散列字段，这样将提高数据均衡分布在每个RegionServer，以实现负载均衡的几率。

- 唯一原则

  需在设计上保证唯一性

  > 使用scan方法时, setStartRow, setEndRow范围越小越好

- 排序原则

  HBase的Rowkey是按照ASCII有序设计的，我们在设计Rowkey时要充分利用这点.

## 4. 表的热点

检索habse的记录首先要通过row key来定位数据行。当大量的client访问hbase集群的一个或少数几个节点，造成少数region server的读/写请求过多、负载过大，而其他region server负载却很小，就造成了“**热点**”现象

解决方案:

- 预分区

- 加盐

  这里所说的加盐不是密码学中的加盐，而是在rowkey的前面增加随机数，具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。

  > 4个region，[,a),[a,b),[b,c),[c,]
  >
  > 原始数据：abc1,abc2,abc3.
  >
  > 加盐后的rowkey：a-abc1,b-abc2,c-abc3

- 哈希

  哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群，但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey，可以使用get操作准确获取某一个行数据

- 反转

  反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分（最没有意义的部分）放在前面。这样可以有效的随机rowkey，但是牺牲了rowkey的有序性。

## 5. 二级索引

HBase表按照rowkey查询性能是最高的。rowkey就相当于hbase表的一级索引！！

为了HBase的数据查询更高效、适应更多的场景，诸如使用非rowkey字段检索也能做到秒级响应，或者支持各个字段进行模糊查询和多字段组合查询等， 因此需要在HBase上面构建二级索引， 以满足现实中更复杂多样的业务需求。

hbase的二级索引其本质就是建立hbase表中列与行键之间的映射关系。

常见的二级索引我们一般可以借助各种其他的方式来实现，例如**Phoenix**或者**solr**或者**ES**等

## 6. 布隆过滤器在hbase的应用

- 布隆过滤器应用

  之前再讲hbase的数据存储原理的时候，我们知道hbase的读操作需要访问大量的文件，大部分的实现通过布隆过滤器来避免大量的读文件操作。

- 布隆过滤器的原理

  通常判断某个元素是否存在用的可以选择hashmap。但是HashMap的实现也有缺点，例如存储容量占比高，考虑到负载因子的存在，通常空间是不能被用满的，而一旦你的值很多例如上亿的时候，那HashMap占据的内存大小就变得很可观了。

**BloomFilter**是一种空间效率很高的随机数据结构，它利用位数组很简洁地表示一个集合，并能判断一个元素是否属于这个集合。hbase中布隆过滤器来过滤指定的rowkey是否在目标文件，避免扫描多个文件。使用布隆过滤器来判断。

布隆过滤器返回true,在结果不一定正确，如果返回false则说明确实不存在。

原理示意图:![示意图](https://img.zhaojq.top/20260727091651489.png "示意图")

见示例代码: 使用guava的BloomFilter












































