HBase 作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和、计数、排序等操作。比如,在旧版本的(<0.92) HBase 中,统计数据表的总行数,需要使用 Counter 方法,执行一次 MapReduce Job 才能得到。
虽然 HBase 在数据存储层中集成了 MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相加或者聚合计算的时候,如果直接将计算过程放置在 server 端,能够减少通讯开销,从而获得很好的性能提升。
于是,HBase 在 0.92 之后引入了协处理器(coprocessors),实现一些激动人心的新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。
类似于传统数据库中的触发器,当发生某些事件的时候这类协处理器会被 Server 端调用。
Observer Coprocessor 就是一些散布在 HBase Server 端代码中的 hook 钩子,在固定的事件发生时被调用。比如:put 操作之前有钩子函数 prePut,该函数在 put 操作执行前会被 Region Server 调用;在 put 操作之后则有 postPut 钩子函数
以 HBase0.92 版本为例,它提供了三种观察者接口:
● RegionObserver:提供客户端的数据操纵事件钩子:Get、Put、Delete、Scan 等。
● WALObserver:提供 WAL 相关操作钩子。
● MasterObserver:提供 DDL-类型的操作钩子。如创建、删除、修改数据表等。
到 0.96 版本又新增一个 RegionServerObserver
其实到现在的 hbase-1.2.6 版本中:Observer 的种类已经很多了:
下图是以 RegionObserver 为例子讲解 Observer 这种协处理器的原理:
1、客户端发出 put 请求
2、该请求被分派给合适的 RegionServer 和 region
3、coprocessorHost 拦截该请求,然后在该表上登记的每个 RegionObserver 上调用 prePut()
4、如果没有被 prePut()拦截,该请求继续送到 region,然后进行处理
5、region 产生的结果再次被 CoprocessorHost 拦截,调用 postPut()
6、假如没有 postPut()拦截该响应,最终结果被返回给客户端
协处理器类似传统数据库中的存储过程,客户端可以调用这些 Endpoint 协处理器执行一段Server 端代码,并将 Server 端代码的结果返回给客户端进一步处理,最常见的用法就是进行聚集操作。如果没有协处理器,当用户需要找出一张表中的最大数据,即 max 聚合操作,就必须进行全表扫描,在客户端代码内遍历扫描结果,并执行求最大值的操作。这样的方法无法利用底层集群的并发能力,而将所有计算都集中到 Client 端统一执行,势必效率低下。利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server端,HBase 将利用底层cluster 的多个节点并发执行求最大值的操作。即在每个 Region 范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该 max 值返回给客户端。在客户端进一步将多个 Region 的最大值进一步处理而找到其中的最大值。这整体的执行效率就会提高很多
下图是 EndPoint 的工作原理:
协处理器的加载方式有两种,我们称之为静态加载方式(Static Load)和动态加载方式(Dynamic Load)。静态加载的协处理器称之为 System Coprocessor,动态加载的协处理器称之为 Table Coprocessor
通过修改 hbase-site.xml 这个文件来实现,启动全局 aggregation,能过操纵所有的表上的数据。只需要添加如下代码:
hbase.coprocessor.user.region.classes
org.apache.hadoop.hbase.coprocessor.AggregateImplementation
为所有 table 加载了一个 coprocessor class,可以用”,”分割加载多个 class
启用表 aggregation,只对特定的表生效。通过 HBase Shell 来实现。
◆disable 指定表。
hbase> disable 'mytable'
◆添加 aggregation
hbase> alter 'mytable', METHOD => 'table_att','coprocessor'=>'|org.apache.Hadoop.hbase.coprocessor.AggregateImplementation||'
◆重启指定表
hbase> enable 'mytable'
只需要三步即可:
disable 'mytable'
alter 'mytable',METHOD=>'table_att_unset',NAME=>'coprocessor$1'
enable 'mytable
由于 HBase 本身没有二级索引(Secondary Index)机制,基于索引检索数据只能单纯地依靠RowKey,为了能支持多条件查询,开发者需要将所有可能作为查询条件的字段一一拼接到RowKey 中,这是 HBase 开发中极为常见的做法。
二级索引的本质就是建立各列值与行键之间的映射关系
在社交类应用中,经常需要快速检索各用户的关注列表 guanzhu,同时,又需要反向检索各种户的粉丝列表 fensi,为了实现这个需求,最佳实践是建立两张互为反向的表:
一个表为正向索引关注表:“guanzhu”:
Rowkey: a
f1:from b
另一个表为反向索引粉丝表:“fensi“:
Rowkey: b
f1:from a
建表语句:
create 'guanzhu','cf1'
create 'fensi','cf1'
实现效果:
往 guanzhu 表插入一条数据
put 'guanzhu','a','cf1:from','b'
就会自动往 fensi 表插入一条数据
put 'fensi','b','cf1:from','a'
插入一条关注信息时,为了减轻应用端维护反向索引表的负担,可用 Observer 协处理器实现:
实现步骤:
1、 编写 TestCoprocessor 代码
package com.ghgj.mazh.hbase.coprocessor;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Durability;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
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;
public class TestCoprocessor extends BaseRegionObserver {
static Configuration config = HBaseConfiguration.create();
static HTable table = null;
static {
config.set("hbase.zookeeper.quorum", "hadoop02:2181,hadoop03:2181,hadoop04:2181,hadoop05:2181");
try {
table = new HTable(config, "guanzhu");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
// super.prePut(e, put, edit, durability);
byte[] row = put.getRow();
Cell cell = put.get("f1".getBytes(), "from".getBytes()).get(0);
Put putIndex = new Put(cell.getValueArray(), cell.getValueOffset(),
cell.getValueLength());
putIndex.addColumn("f1".getBytes(), "from".getBytes(), row);
table.put(putIndex);
table.close();
}
}
2、 打成 jar 包(cppp.jar),上传到 hdfs 中的 hbasecp 目录下
[hadoop@hadoop02 soft]# hadoop fs -mkdir -p /hbasecp
[hadoop@hadoop02 soft]# hadoop fs -put cppp.jar /hbasecp
3、 建 hbase 表,请按以下顺序操作
hbase(main):036:0> create 'guanzhu','f1'
hbase(main):036:0> create 'fensi','f1'
hbase(main):036:0> disable 'fensi'
hbase(main):036:0> alter 'fensi',METHOD => 'table_att','coprocessor' =>
'hdfs://myha01/hbasecp/cppp.jar|com.ghgj.mazh.hbase.coprocessor.TestCoprocessor|1001 |'
#理解 coprocessor 的四个参数,分别用'|'隔开的
1、 你的协处理器 jar 包所在 hdfs 上的路径
2、 协处理器类全限定名
3、 协处理器加载顺序
4、 传参
hbase(main):036:0> enable 'fensi'