大数据开发高薪训练营--HBase协处理

       最近大数据技术陷入了瓶颈,偶然间发现拉钩大数据开发高薪训练营课程,主要是被他的用户画像匹配系统吸引,据说是用拉钩真实数据进行讲解。本来对一些基础知识包括mapreduce,hive,hbase等讲解没有报太多希望,没想到听了几节课后,我反而发现有很多知识点我这么多年都忽略了。市面上的很多课程我也都听了,讲的内容大多数都是只有宽度没有深度,拉钩教育确实与众不同,我想主要是他的授课模式的创新,类似好未来那种 名师讲解 + 小班答疑。拉钩教育便是采用的这种模式,知识点都是知名讲师先录好,学生在线自主学习,完成每章作业后开锁新课程,拉钩会根据全体学员进度,收集同学们的疑问然后进行在线直播内容回顾和答疑以及作业讲解,同时平时有问题可以在微信群里问,有专门的老师进行解答。

       言归正传,本篇主要是介绍HBase协处理器,并通过两个小案例讲解一下具体使用过程。

那为什么要用协处理器?

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

        此时就可以考虑使用Coprocessor(协处理器)。将业务运算代码封装到Coprocessor中并在RegionServer上运行,即在数据实际存储位置执行,最后将运算结果返回到客户端。利用协处理器,用户可以编写运行在 HBase Server 端的代码。说白了就是讲客户端代码程序放到各个集群去处理,最后结果返回客户端。       

Hbase Coprocessor类似以下概念

触发器和存储过程:一个Observer Coprocessor有些类似于关系型数据库中的触发器,通过它我们可以在一些事件(如Get或是Scan)发生前后执行特定的代码。Endpoint Coprocessor则类似于关系型数据库中的存储过程,因为它允许我们在RegionServer上直接对它存储的数据进行运算,而非是在客户端完成运算。

MapReduceMapReduce的原则就是将运算移动到数据所处的节点。Coprocessor也是按照相同的原则去工作的。

AOP:如果熟悉AOP的概念的话,可以将Coprocessor的执行过程视为在传递请求的过程中对请求进行了拦截,并执行了一些自定义代码。

协处理器类型

Observer

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

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

Endpoint

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

Endpoint常见用途

聚合操作

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

使用Endpoint Coprocessor,用户可以将求最大值的代码部署到 HBase RegionServer 端,HBase 会利用集群中多个节点的优势来并发执行求最大值的操作。也就是在每个 Region 范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该 max 值返回给Client。在Client进一步将多个 Region 的最大值汇总进一步找到全局的最大值。Endpoint Coprocessor的应用我们后续可以借助于Phoenix非常容易就能实现。针对Hbase数据集进行聚合运算直接使用SQL语句就能搞定。

案例1

        课上讲了一个简单的Observer案例,通过协处理器Observer实现Hbase当中t1表插入数据,指定的另一张表t2也需要插入相对应的数据。(Hbase用的版本是1.3.1)

create 't1','info'

create 't2','info'

开发步骤

1. 编写 Observer 协处理器
package com.lagou.coprocessor;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CoprocessorEnvironment;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
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.filter.ByteArrayComparable;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import java.io.IOException;
public class MyProcessor extends BaseRegionObserver {
@Override
public void prePut(ObserverContext<RegionCoprocessorEnvironment> ce, Put put, WALEdit edit, Durability durability)
throws IOException {
    //把自己需要执行的逻辑定义在此处,向t2表插入数据,数据具体是什么内容与Put一样
    final HTableInterface t2 = e.getEnvironment().getTable(TableName.valueOf("t2"));
    //解析t1表的插入对象put
    final Cell cell = put.get(Bytes.toBytes("info"), Bytes.toBytes("name")).get(0);
    //table对象.put
    final Put put1 = new Put(put.getRow());
    put1.add(cell);
    t2.put(put1); //执行向t2表插入数据
    t2.close();
    }
}

 添加依赖

<!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-server --> 
<dependency> 
    <groupId>org.apache.hbase</groupId> 
    <artifactId>hbase-server</artifactId> 
    <version>1.3.1</version>
</dependency>

2. 打成Jar包,上传HDFS

cd /opt/lagou/softwares
mv original-hbaseStudy-1.0-SNAPSHOT.jar processor.jar 
hdfs dfs -mkdir -p /processor 
hdfs dfs -put processor.jar /processor
3. 挂载协处理器
hbase(main):056:0> describe 't1' 
hbase(main):055:0> alter 't1',METHOD => 'table_att','Coprocessor'=>'hdfs://linux121:9000/processor/processor.jar|com.lagou.hbase.processor.MyProcessor|1001|' #再次查看't1'表,
hbase(main):043:0> describe 't1'
4. 验证协处理器
t1 表中插入数据 (shell 方式验证 )
 
hbase(main):043:0> put 't1','rk1','info:name','lisi' 
这样t1插入一条数据后,查看t2也插入了一条相同的数据。
 
5. 卸载协处理器
disable 't1' 
alter 't1',METHOD=>'table_att_unset',NAME=>'coprocessor$1'
enable 't2'

案例2

        案例2是作业。

        在社交网站,社交APP上会存储有大量的用户数据以及用户之间的关系数据,比如A用户的好友列表会展示出他所有的好友,现有一张Hbase表,存储就是当前注册用户的好友关系数据,如下

  

需求

  1. 使用Hbase相关API创建一张结构如上的表

  2. 删除好友操作实现(好友关系双向,一方删除好友,另一方也会被迫删除好友)

    例如:uid1用户执行删除uid2这个好友,则uid2的好友列表中也必须删除uid1

代码如下:包括创建表,添加数据以及删除好友关系

import org.apache.directory.shared.kerberos.codec.apRep.actions.ApRepInit;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

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

public class HbaseCreateTable {
    Configuration conf=null;
    Connection conn=null;
    HBaseAdmin admin =null;
    @Before
    public void init () throws IOException {
        conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","node01");
        conf.set("hbase.zookeeper.property.clientPort","2181");
        conn = ConnectionFactory.createConnection(conf);
    }

    //创建表
    @Test
    public void createTable() throws IOException {
        admin = (HBaseAdmin) conn.getAdmin();
        //创建表描述器
        HTableDescriptor teacher = new HTableDescriptor(TableName.valueOf("relationship"));
        //设置列族描述器
        teacher.addFamily(new HColumnDescriptor("friends"));
        //执⾏创建操作
        admin.createTable(teacher);
        System.out.println("relationship表创建成功!!");
    }

    // 添加数据
    @Test
    public void putData() throws IOException {
        //获取table 对象
        Table table = conn.getTable(TableName.valueOf("relationship"));
        List<Put> puts = new ArrayList<Put>();
        // rowkey  uid1
        Put put = new Put(Bytes.toBytes("uid1"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid2"),Bytes.toBytes("uid2"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid5"),Bytes.toBytes("uid5"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid7"),Bytes.toBytes("uid7"));
        puts.add(put);

        // rowkey uid2
        put = new Put(Bytes.toBytes("uid2"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid1"),Bytes.toBytes("uid1"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid3"),Bytes.toBytes("uid3"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid6"),Bytes.toBytes("uid6"));
        puts.add(put);

        // rowkey uid3
        put = new Put(Bytes.toBytes("uid3"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid2"),Bytes.toBytes("uid2"));
        puts.add(put);

        // rowkey uid5
        put = new Put(Bytes.toBytes("uid5"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid1"),Bytes.toBytes("uid1"));
        puts.add(put);

        // rowkey uid6
        put = new Put(Bytes.toBytes("uid6"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid2"),Bytes.toBytes("uid2"));
        puts.add(put);

        // rowkey uid7
        put = new Put(Bytes.toBytes("uid7"));
        put.addColumn(Bytes.toBytes("friends"),Bytes.toBytes("uid1"),Bytes.toBytes("uid1"));
        puts.add(put);

        System.out.println(puts);

        table.put(puts);


        //关闭table对象
        table.close();
        System.out.println("插⼊成功!!");
    }

    //删除数据
    @Test
    public void deleteData() throws IOException {
        //需要获取⼀个table对象
        Table table = conn.getTable(TableName.valueOf("relationship"));
        //准备delete对象
        final Delete delete = new Delete(Bytes.toBytes("uid1"));
//执⾏table
        table.delete(delete);
        //关闭table对象
        table.close();
        System.out.println("删除数据成功!!");
    }



    /**
     * 全表扫描
     */
    @Test
    public void scanAllData() throws IOException {
        HTable table = (HTable) conn.getTable(TableName.valueOf("relationship"));
        Scan scan = new Scan();
        ResultScanner resultScanner = table.getScanner(scan);
        for (Result result : resultScanner) {
            Cell[] cells = result.rawCells();//获取改⾏的所有cell对象
            for (Cell cell : cells) {
                //通过cell获取rowkey,cf,column,value
                String cf = Bytes.toString(CellUtil.cloneFamily(cell));
                String column = Bytes.toString(CellUtil.cloneQualifier(cell));
                String value = Bytes.toString(CellUtil.cloneValue(cell));
                String rowkey = Bytes.toString(CellUtil.cloneRow(cell));
                System.out.println(rowkey + "----" + cf + "--" + column + "---" + value);
            }
        }
        table.close();
    }

    @After
    public void destroy(){
        if(admin!=null){
            try {
                admin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(conn !=null){
            try {
                conn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void deleteFriends(String uid,String friend) throws IOException {
        Table table = conn.getTable(TableName.valueOf("relationship"));
        Delete delete = new Delete(Bytes.toBytes(uid));
        delete.addColumn(Bytes.toBytes("friends"),Bytes.toBytes(friend));
        table.delete(delete);
    }

    @Test
    public void deleteF() throws IOException {
        deleteFriends("uid1","uid2");
    }

}

协处理代码

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.TableName;
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;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;

public class DeleteRalation extends BaseRegionObserver {
    @Override
    public void postDelete(ObserverContext<RegionCoprocessorEnvironment> env, Delete delete, WALEdit edit, Durability durability) throws IOException {
        HTableInterface relationship = env.getEnvironment().getTable(TableName.valueOf("relationship"));
        // 获取删除的rowkey
        byte[] row = delete.getRow();
        NavigableMap<byte[], List<Cell>> familyCellMap = delete.getFamilyCellMap();
        Set<Map.Entry<byte[], List<Cell>>> entries = familyCellMap.entrySet();
        for (Map.Entry<byte[], List<Cell>> entry:entries){
            System.out.println("====================" + Bytes.toString(entry.getKey()));
            List<Cell> cells = entry.getValue();
            for (Cell cell :cells){
                byte[] rowkey = CellUtil.cloneRow(cell);
                byte[] column = CellUtil.cloneQualifier(cell);
                System.out.println("rowkey ============= " + rowkey);
                System.out.println("column ============= " + column);
                //判断要删除的是否存在
                boolean flag = relationship.exists(new Get(column).addColumn(Bytes.toBytes("friends"),rowkey));
                if(flag){
                    System.out.println("开始删除 ======================== " + column + "  , " + rowkey );
                    Delete deleteF = new Delete(column).addColumn(Bytes.toBytes("friends"), rowkey);
                    relationship.delete(deleteF);
                }
            }

        }
        relationship.close();

    }
}

然后也是向上一步进行协处理注册,不过最好注册后重启一下hbase服务。

代码及视频地址

https://gitee.com/shareshow/lagou.git

演示视频,删除uid1里的friends:uid2后,可以看到uid2里的frends:uid1好友关系也自动被删除

https://www.bilibili.com/video/BV1vA411Y7v9/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值