HBase利用observer(协处理器)创建二级索引
一、协处理器—Coprocessor
1、 起源
Hbase 作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执 行求和、计数、排序等操作。比如,在旧版本的(<0.92)Hbase 中,统计数据表的总行数,需 要使用 Counter 方法,执行一次 MapReduce Job 才能得到。虽然 HBase 在数据存储层中集成
了 MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相 加或者聚合计算的时候, 如果直接将计算过程放置在 server 端,能够减少通讯开销,从而获 得很好的性能提升。于是, HBase 在 0.92 之后引入了协处理器(coprocessors),实现一些激动
人心的新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。
2、协处理器有两种: observer 和 endpoint
(1) Observer 类似于传统数据库中的触发器,当发生某些事件的时候这类协处理器会被 Server 端调用。Observer Coprocessor 就是一些散布在 HBase Server 端代码中的 hook 钩子, 在固定的事件发生时被调用。比如: put 操作之前有钩子函数 prePut,该函数在 put 操作
执行前会被 Region Server 调用;在 put 操作之后则有 postPut 钩子函数。
下图是以 RegionObserver 为例子讲解 Observer 这种协处理器的原理:
下图是 EndPoint 的工作原理:
(3)总结
Observer 允许集群在正常的客户端操作过程中可以有不同的行为表现
Endpoint 允许扩展集群的能力,对客户端应用开放新的运算命令
observer 类似于 RDBMS 中的触发器,主要在服务端工作
endpoint 类似于 RDBMS 中的存储过程,主要在 client 端工作
observer 可以实现权限管理、优先级设置、监控、 ddl 控制、 二级索引等功能
endpoint 可以实现 min、 max、 avg、 sum、 distinct、 group by 等功能
二、协处理器加载方式
协处理器的加载方式有两种,我们称之为静态加载方式( Static Load) 和动态加载方式 ( Dynamic Load)。 静态加载的协处理器称之为 System Coprocessor,动态加载的协处理器称 之为 Table Coprocessor
1、静态加载
通过修改 hbase-site.xml 这个文件来实现, 启动全局 aggregation,能过操纵所有的表上 的数据。只需要添加如下代码:
1 2 3 4 |
|
为所有 table 加载了一个 cp class,可以用” ,”分割加载多个 class
2、动态加载
启用表 aggregation,只对特定的表生效。通过 HBase Shell 来实现。
disable 指定表。 hbase> disable 'mytable'
添加 aggregation
hbase> alter 'mytable', METHOD => 'table_att','coprocessor'=>
'|org.apache.Hadoop.hbase.coprocessor.AggregateImplementation||'
重启指定表 hbase> enable 'mytable'
3、协处理器卸载
row key 在 HBase 中是以 B+ tree 结构化有序存储的,所以 scan 起来会比较效率。单表以 row key 存储索引, column value 存储 id 值或其他数据 ,这就是 Hbase 索引表的结构。
由于 HBase 本身没有二级索引( Secondary Index)机制,基于索引检索数据只能单纯地依靠 RowKey,为了能支持多条件查询,开发者需要将所有可能作为查询条件的字段一一拼接到 RowKey 中,这是 HBase 开发中极为常见的做法
代码实现
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Durability;
import org.apache.hadoop.hbase.client.HTableInterface;
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;
import java.io.IOException;
import java.util.List;
/**
* 创建二级索引
* rowKey age name
* 由age找到rowKey再找name
* 创建过程:rowKey name age i_age
* i_age age:rowkey
* 将 i_age 作为rowKey
*
* r1 zs 12
* r2 lisi 13
* r3 tom 12
*
* i_12 r1,r2
* i_13 r3
*/
public class IndexObServer extends BaseRegionObserver {
/**
*
* ObserverContext:上下文
* @param e
* @param put 向hbaseput的数据
* @param edit :写日志
* @param durability :持久化
* @throws IOException
*/
@Override
public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
//super.prePut(e, put, edit, durability);
//获取--rowkey
byte[] rowkey = put.getRow();
//获取--age
//byte[] ages = put.getAttribute("age");
List<Cell> cells = put.get("info".getBytes(), "age".getBytes());
for (Cell cell :
cells) {
byte[] age = cell.getValue();
//获取表名
TableName tableName = e.getEnvironment().getRegion().getTableDesc().getTableName();
//表对象
HTableInterface table = e.getEnvironment().getTable(tableName);
Put indexPut = new Put(age).addColumn("info".getBytes(), "i_age_rowkey".getBytes(), rowkey);
table.put(indexPut);
}
}
}
写类--->打包----->上传(打成 jar 包( cppp.jar),上传到 hdfs 中的 hbasecp 目录下)----->建hbase表