时间:2021-07-01 10:21:17 帮助过:31人阅读
1.起因(Why HBase Coprocessor) HBase作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和、计数、排序等操作。比如,在旧版本的(0.92)Hbase中,统计数据表的总行数,需要使用Counter方法,执行一次MapReduce Job才能得到。虽
HBase作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和、计数、排序等操作。比如,在旧版本的(<0.92)Hbase中,统计数据表的总行数,需要使用Counter方法,执行一次MapReduce Job才能得到。虽然HBase在数据存储层中集成了MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相加或者聚合计算的时候,如果直接将计算过程放置在server端,能够减少通讯开销,从而获得很好的性能提升。于是,HBase在0.92之后引入了协处理器(coprocessors),实现一些激动人心的新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。
HBase协处理器的灵感来自于Jeff Dean 09年的演讲( P66-67)。它根据该演讲实现了类似于bigtable的协处理器,包括以下特性:
协处理器分两种类型,系统协处理器可以全局导入region server上的所有数据表,表协处理器即是用户可以指定一张表使用协处理器。协处理器框架为了更好支持其行为的灵活性,提供了两个不同方面的插件。一个是观察者(observer),类似于关系数据库的触发器。另一个是终端(endpoint),动态的终端有点像存储过程。
观察者的设计意图是允许用户通过插入代码来重载协处理器框架的upcall方法,而具体的事件触发的callback方法由HBase的核心代码来执行。协处理器框架处理所有的callback调用细节,协处理器自身只需要插入添加或者改变的功能。
以HBase0.92版本为例,它提供了三种观察者接口:
这些接口可以同时使用在同一个地方,按照不同优先级顺序执行.用户可以任意基于协处理器实现复杂的HBase功能层。HBase有很多种事件可以触发观察者方法,这些事件与方法从HBase0.92版本起,都会集成在HBase API中。不过这些API可能会由于各种原因有所改动,不同版本的接口改动比较大,具体参考Java Doc。
RegionObserver工作原理,如图1所示。更多关于Observer细节请参见HBaseBook的第9.6.3章节。
图1 RegionObserver工作原理
终端是动态RPC插件的接口,它的实现代码被安装在服务器端,从而能够通过HBase RPC唤醒。客户端类库提供了非常方便的方法来调用这些动态接口,它们可以在任意时候调用一个终端,它们的实现代码会被目标region远程执行,结果会返回到终端。用户可以结合使用这些强大的插件接口,为HBase添加全新的特性。终端的使用,如下面流程所示:
整体的终端调用过程范例,如图2所示:
图2 终端调用过程范例
在该实例中,我们通过计算HBase表中行数的一个实例,来真实感受协处理器 的方便和强大。在旧版的HBase我们需要编写MapReduce代码来汇总数据表中的行数,在0.92以上的版本HBase中,只需要编写客户端的代码即可实现,非常适合用在WebService的封装上。
我们有两个方法:1.启动全局aggregation,能过操纵所有的表上的数据。通过修改hbase-site.xml这个文件来实现,只需要添加如下代码:
<property> <name>hbase.coprocessor.user.region.classesname> <value>org.apache.hadoop.hbase.coprocessor.AggregateImplementationvalue> property>
2.启用表aggregation,只对特定的表生效。通过HBase Shell 来实现。
(1)disable指定表。hbase> disable 'mytable'
(2)添加aggregation hbase> alter 'mytable', METHOD => 'table_att','coprocessor'=>'|org.apache.hadoop.hbase.coprocessor.AggregateImplementation||'
(3)重启指定表 hbase> enable 'mytable'
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.coprocessor.AggregationClient; import org.apache.hadoop.hbase.client.coprocessor.LongColumnInterpreter; import org.apache.hadoop.hbase.coprocessor.ColumnInterpreter; import org.apache.hadoop.hbase.util.Bytes; public class MyAggregationClient { private static final byte[] TABLE_NAME = Bytes.toBytes("bigtable1w"); private static final byte[] CF = Bytes.toBytes("bd"); public static void main(String[] args) throws Throwable { Configuration customConf = new Configuration(); customConf.set("hbase.zookeeper.quorum", "192.168.58.101"); //提高RPC通信时长 customConf.setLong("hbase.rpc.timeout", 600000); //设置Scan缓存 customConf.setLong("hbase.client.scanner.caching", 1000); Configuration configuration = HBaseConfiguration.create(customConf); AggregationClient aggregationClient = new AggregationClient( configuration); Scan scan = new Scan(); //指定扫描列族,唯一值 scan.addFamily(CF); //long rowCount = aggregationClient.rowCount(TABLE_NAME, null, scan); long rowCount = aggregationClient.rowCount(TableName.valueOf("bigtable1w"), new LongColumnInterpreter(), scan); System.out.println("row count is " + rowCount); } }
协处理器其中的一个作用是使用Observer创建二级索引。先举个实际例子:
我们要查询指定店铺指定客户购买的订单,首先有一张订单详情表,它以被处理后的订单id作为rowkey;其次有一张以客户nick为rowkey的索引表,结构如下:
rowkey family
dp_id+buy_nick1 tid1:null tid2:null ...
dp_id+buy_nick2 tid3:null
...
该表可以通过Coprocessor来构建,实例代码:
put操作效率不高,如上代码,每插入一条数据需要创建一个新的索引表连接(可以使用htablepool优化),向索引表插入数据。即耗时是双倍的,对hbase的集群的压力也是双倍的。当索引表有多个时,压力会更大。
查询效率比filter高,毫秒级别,因为都是rowkey的查询。
如上是估计的效率情况,需要根据实际业务场景和集群情况而定,最好做预先测试。