用Hbase协处理器Observer实现二次索引

需求

有一张uuid表,columnphone,要求在插入uuid数据的时候,会有一个通过phone指向uuid的索引表。本文通过Observer的的动态装载协处理方式实现

实现

HBase创建uuid表和phone表

create 'phone','f1'
create 'uuid','f1'

maven依赖

<dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>

Java代码

package com.me.hbase.Coprocessor;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
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;

import java.io.IOException;
import java.util.List;

public class TextObserver extends BaseRegionObserver {
    static Connection connection = null;
    static Table table = null;

    private static final String FROM_FAMAILLY_NAME = "f1";
    private static final String FROM_QUALIFIER_NAME = "phone";

    private static final String TO_TABLE_NAME = "phone";
    private static final String TO_FAMAILLY_NAME = "f1";
    private static final String TO_QUALIFIER_NAME = "uuid";
    static{
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","slave2:2181");
        try {
            connection = ConnectionFactory.createConnection(conf);
            table = connection.getTable(TableName.valueOf(TO_TABLE_NAME));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void prePut(final ObserverContext<RegionCoprocessorEnvironment> e, final Put put, final WALEdit edit, final Durability durability) throws IOException {
        try {
            //通过put对象获取插入数据的rowkey
            byte[] rowBytes = put.getRow();
            String rowkey = Bytes.toString(rowBytes);

            List<Cell> list = put.get(Bytes.toBytes(FROM_FAMAILLY_NAME), Bytes.toBytes(FROM_QUALIFIER_NAME));
            if (list == null || list.size() == 0) {
                return;
            }

            Cell cell2 = list.get(0);

            //通过cell获取数据值
            String stringValue = Bytes.toString(CellUtil.cloneValue(cell2));
            //创建put对象,将二次索引插入进phone表
            Put put2 = new Put(stringValue.getBytes());
            put2.addColumn(Bytes.toBytes(TO_FAMAILLY_NAME), Bytes.toBytes(TO_QUALIFIER_NAME), rowkey.getBytes());
            table.put(put2);
            table.close();
        } catch (Exception e1) {
            return ;
        }
    }
}

java代码打包之后,放入HDFS

hdfs dfs -put /jar包路径..../hbase_observer-1.0-SNAPSHOT.jar /observer.jar
hdfs dfs -chmod 777 /observer.jar

Hbase动态装载协处理器

alter 'uuid', METHOD=> 'table_att', 'Coprocessor'=>'hdfs://master:9000/observer.jar|com.me.hbase.Coprocessor.TextObserver|1001'

试验

hbase执行如下命令

//禁用
disable 'uuid'
//安装
alter 'uuid', METHOD=> 'table_att', 'Coprocessor'=>'hdfs://master:9000/observer.jar|com.me.hbase.Coprocessor.TextObserver|1001'
//启用
enable 'uuid'
//试验插入数据
put 'uuid','uuid1','f1:phone','13811111111'

  

'Coprocessor'=>'hdfs://master:9000/observer.jar|com.me.hbase.Coprocessor.TextObserver|1001'参数说明:

  • 一共四个参数,用|分割,最后一个params可省略
  • jar_file_path,Java代码打成的jar包存放的绝对路径,最好是HDFS路径
  • observer_class_path,observer类的包名加类名
  • priority,优先级,就用固定的1001即可
  • params,传递给observer的参数信息,相当于map,例如:id=123,name='hahaha',age=18

安装注意:

一定要把参数填对,路径填准确,不然会出现报如下错误:

ERROR: org.apache.hadoop.hbase.DoNotRetryIOException: Class com.me.hbase.Coprocessor.TextObserver cannot be loaded Set hbase.table.sanity.checks to false at conf or table descriptor if you want to bypass sanity checks
at org.apache.hadoop.hbase.master.HMaster.warnOrThrowExceptionForFailure(HMaster.java:1819)
at org.apache.hadoop.hbase.master.HMaster.sanityCheckTableDescriptor(HMaster.java:1680)
at org.apache.hadoop.hbase.master.HMaster.modifyTable(HMaster.java:2207)
at org.apache.hadoop.hbase.master.MasterRpcServices.modifyTable(MasterRpcServices.java:1188)
at org.apache.hadoop.hbase.protobuf.generated.MasterProtos$MasterService$2.callBlockingMethod(MasterProtos.java:58547)
at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:2339)
at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:123)
at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:188)
at org.apache.hadoop.hbase.ipc.RpcExecutor$Handler.run(RpcExecutor.java:168)

 

测试结果:

 可以看到向uuid插入一条数据的时候,phone表自动会加一条指向uuid rowkey的数据

卸载协处理器方式

//禁用
disable 'uuid'
//卸载
alter 'uuid',METHOD=>'table_att_unset',NAME=>'coprocessor$1'
//启用
enable 'uuid'

扩展理论知识

Hbase 协处理器分2种:

1.Observer,类似于传统数据库的触发器。Observer会在固定的事件发生时被调用,比如:put操作之前有钩子函数 prePut,该函数在 put 操作
执行前会被 Region Server 调用;在 put 操作之后则有 postPut 钩子函数

2.Endpoint,类似于传统数据库的存储过程。客户端可以调用这些 Endpoint 协处理器执行一段 Server 端代码,并将 Server 端代码的结果返回给客户端进一步处理,最常见的用法就是进行聚集操作。如果没有协处理器,当用户需要找出一张表中的最大数据,即max 聚合操作,就必须进行全表扫描,在客户端代码内遍历扫描结果,并执行求最大值的操作。这样的方法无法利用底层集群的并发能力,而将所有计算都集中到 Client 端统一执 行,势必效率低下。

协处理器详细理论:https://www.cnblogs.com/liuwei6/p/6837674.html

HBase Observer协处理装载方式分2种:

1.静态装载Coprocessor

如果一个Coprocessor是静态装载的,要卸载它就需要重启HBase。

2.动态装载Coprocessor

动态装载Coprocessor的一个优势就是不需要重启HBase。不过动态装载的Coprocessor只是针对某个表有效。因此,动态装载的Coprocessor又被称为表级Coprocessor。

详情参考:https://www.jianshu.com/p/d56584c45401

 

原文地址:https://www.cnblogs.com/Alcesttt/p/13068932.html