HBase协处理器及二级索引

1. HBase协处理器

  • 官网文档
  • 起源:
    • Hbase 作为列族数据库的缺点:
      • 无法轻易建立“二级索引”
      • 难以执行求和、计数、排序等操作。
    • 虽然 HBase 在数据存储层中集成了 MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相加或者聚合计算的时候, 如果直接将计算过程放置在 server端,能够减少通讯开销,从而获得很好的性能提升。
    • 于是, HBase 在 0.92 之后引入了协处理器(coprocessors),实现一些新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。

1.1 两种协处理器

  • 协处理器有两种:observer和endpoint

1.1.1 observer

  • Observer 类似于传统数据库中的触发器,当发生某些事件的时候这类协处理器会被Server 端调用。
  • Observer Coprocessor就是一些散布在HBase Server端代码中的 hook 钩子,在固定的事件发生时被调用。
    • 比如: put 操作之前有钩子函数 prePut,该函数在put操作执行前会被Region Server调用;在 put 操作之后则有 postPut 钩子函数
  • 以 HBase0.92 版本为例,它提供了三种observer观察者接口:
    • RegionObserver:提供客户端的数据操纵事件钩子: Get、 Put、 Delete、 Scan 等。
    • WALObserver:提供 WAL 相关操作钩子。
    • MasterObserver:提供 DDL类型的操作钩子。如创建、删除、修改数据表等。
    • 到 0.96 版本又新增一个 RegionServerObserver

 

  • 下图是以 RegionObserver 为例子讲解 Observer 这种协处理器的原理:

 

1.1.2 endpoint

  • Endpoint协处理器类似传统数据库中的存储过程,客户端可以调用这些 Endpoint 协处理器执行一段 Server 端代码,并将 Server 端代码的结果返回给客户端进一步处理
  • 最常见的用法就是进行聚集操作。
    • 如果没有协处理器,当用户需要找出一张表中的最大数据,即max 聚合操作,就必须进行全表扫描,在客户端代码内遍历扫描结果,并执行求最大值的操作。
    • 这样的方法无法利用底层集群的并发能力,而将所有计算都集中到 Client 端统一执行,势必效率低下。
    • 利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server 端,HBase将利用底层cluster 的多个节点并发执行求最大值的操作。即在每个 Region范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该max值返回给客户端。
    • 在客户端进一步将多个Region的最大值进一步处理而找到其中的最大值。这样整体的执行效率就会提高很多

1.1.3 总结

  • Observer允许集群在正常的客户端操作过程中可以有不同的行为表现
  • Endpoint允许扩展集群的能力,对客户端应用开放新的运算命令
  • observer类似于 RDBMS 中的触发器,主要在服务端工作
  • endpoint类似于 RDBMS 中的存储过程,主要在 client 端工作
  • observer可以实现权限管理、优先级设置、监控、 ddl 控制、 二级索引等功能
  • endpoint可以实现 min、 max、 avg、 sum、 distinct、 group by等功能

1.2 协处理器加载方式

  • 协处理器的加载方式有两种
    • 静态加载方式( Static Load);静态加载的协处理器称之为 System Coprocessor
    • 动态加载方式 ( Dynamic Load);动态加载的协处理器称 之为 Table Coprocessor

1.2.1 静态加载

  • 通过修改 hbase-site.xml 这个文件来实现, 如启动全局 aggregation,能过操纵所有的表数据。只需要在hbase-site.xml里面添加以下配置即可
  • 注意:修改完配置之后需要重启HBase集群
  • 如下,静态加载RegionObserver和endpoint

<property>
        <name>hbase.coprocessor.region.classes</name>
        <value>org.apache.hadoop.hbase.coprocessor.AggregateImplementation</value>
</property>

  • 为所有table加载了一个 cp class协处理器的类;此外可以用” ,”分割加载多个 class
  • 另外,属性hbase.coprocessor.wal.classes用于加载WALObservers
  • 属性hbase.coprocessor.master.classes用于加载MasterObservers

1.2.2 动态加载

  • 两种方式:①通过hbase shell;②通过编程
  • 若要实现启用表aggregation,只对特定的表生效。
  • 方式一:通过 HBase Shell 来实现。
    • disable 指定表。

disable 'mytable'

添加 aggregation

alter 'mytable', METHOD => 'table_att', 'Coprocessor'=>'hdfs://<namenode>:<port>/
user/<hadoop-user>/coprocessor.jar| org.myname.hbase.Coprocessor.RegionObserverExample|1073741823|
arg1=1,arg2=2'

 

重启指定表

enable 'mytable'

  • 方式二:通过编码


Path path = new Path("hdfs://coprocessor_path");
TableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
hTableDescriptor.addCoprocessor(RegionObserverExample.class.getCanonicalName(), path, Coprocessor.PRIORITY_USER, null);
...

1.2.3 卸载协处理器

 

1.3 协处理器Observer实战

 

xdfsdfsdf

  • 通过协处理器Observer实现向hbase当中一张表插入数据时,将数据复制一份保存到另外一张表当中去;但是只取第一张表当中的部分列数据,保存到第二张表当中去

1.3.1 创建第一张表proc1

  • 打开hbase shell

cd /install/hbase-1.2.0-cdh5.14.2/
bin/hbase shell

  • 在HBase当中创建一张表,表名proc1,并只有一个列族info

hbase(main):053:0> create 'proc1','info'

1.3.2 创建第二张表proc2

  • 创建第二张表proc2,作为目标表
  • 将第一张表当中插入数据的部分列,使用协处理器,复制到proc2表当中来

hbase(main):054:0> create 'proc2','info'

1.3.3 开发HBase协处理器

  • 创建maven工程所用的repositories、dependencies、plugins跟之前的一样

    <repositories>
        <repository>
            <id>cloudera</id>
            <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>2.6.0-mr1-cdh5.14.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.2.0-cdh5.14.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.2.0-cdh5.14.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <!--    <verbal>true</verbal>-->
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                      <exclude>META-INF/*.SF</exclude>
                                      <exclude>META-INF/*.DSA</exclude>
                                      <exclude>META-INF/*/RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

  • 开发HBase的协处理器

public class MyProcessor extends BaseRegionObserver {
    /**
     *
     * @param e
     * @param put   插入到proc1表里面的数据,都是封装在put对象里面了,可以解析put对象,获取数据,获取到了数据之后,插入到proc2表里面去
     * @param edit
     * @param durability
     * @throws IOException
     */
    @Override
    public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
        //获取连接
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181");
        Connection connection = ConnectionFactory.createConnection(configuration);

        //涉及到多个版本问题
        List<Cell> cells = put.get("info".getBytes(), "name".getBytes());
        Cell nameCell = cells.get(0);//获取最新的那个版本数据
        //Cell nameCell = put.get("info".getBytes(), "name".getBytes()).get(0);

        Table proc2 = connection.getTable(TableName.valueOf("proc2"));
        
        Put put1 = new Put(put.getRow());
        put1.add(nameCell);
        
        proc2.put(put1);
        proc2.close();
        connection.close();
    }
}

1.3.4 将项目打成jar包,并上传到HDFS上面

  • 将我们的协处理器打成一个jar包,此处不需要用任何的打包插件即可
  • 然后将打好的jar包上传到linux的/kkb/install路径下
  • 再将jar包上传到HDFS

cd /install
mv original-hbaseStudy-1.0-SNAPSHOT.jar  processor.jar
hdfs dfs -mkdir -p /processor
hdfs dfs -put processor.jar /processor

1.3.5 将jar包挂载到proc1表

hbase(main):056:0> describe 'proc1'
hbase(main):055:0> alter 'proc1', METHOD => 'table_att', 'Coprocessor'=>'hdfs://node01:8020/processor/processor.jar|com.kkb.hbasemr.MyProcessor|1001|'

注意:根据自己的实际情况,修改全类名com.kkb.hbasemr.MyProcessor

  • 再次查看'proc1'表;可以查看到我们的卸载器已经加载了

hbase(main):043:0> describe 'proc1'

1.3.6 向proc1表添加数据


public class TestObserver {
    @Test
    public void testPut() throws Exception{
        //获取连接
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.quorum", "node01,node02");

        Connection connection = ConnectionFactory.createConnection(configuration);

        Table proc1 = connection.getTable(TableName.valueOf("proc1"));

        Put put1 = new Put(Bytes.toBytes("hello_world"));

        put1.addColumn(Bytes.toBytes("info"),"name".getBytes(),"helloworld".getBytes());
        put1.addColumn(Bytes.toBytes("info"),"gender".getBytes(),"abc".getBytes());
        put1.addColumn(Bytes.toBytes("info"),"nationality".getBytes(),"test".getBytes());
        proc1.put(put1);
        byte[] row = put1.getRow();
        System.out.println(Bytes.toString(row));
        proc1.close();
        connection.close();
    }
}

  • 注意:如果需要卸载我们的协处理器,那么进入hbase的shell命令行,执行以下命令即可

disable 'proc1'
alter 'proc1', METHOD=>'table_att_unset', NAME=>'coprocessor$1'
enable 'proc1'

2. HBase的数据备份

2.1 基于HBase提供的类对表进行备份

  • 使用HBase提供的类把HBase中某张表的数据导出到HDFS,之后再导出到测试hbase表中。
  • (1) 从hbase表导出到HDFS
  1. shells]$ hbase org.apache.hadoop.hbase.mapreduce.Export myuser /hbase_data/myuser_bak
  • (2) 文件导入hbase表

      shell中创建备份目标表

'myuser_bak','f1','f2'

  • 将HDFS上的数据导入到备份目标表中

           org.apache.hadoop.hbase.mapreduce.Driver import myuser_bak /_data/myuser_bak/*

  • 补充说明
  • 增量数据备份,增量备份跟全量备份操作差不多,只不过要在后面加上时间戳。

  • HBase数据导出到HDFS

              org.apache.hadoop.hbase.mapreduce.Export test /hbase_data/test_bak_increment 开始时间戳  结束时间戳

2.2 基于snapshot快照对表进行备份

  • 通过snapshot快照的方式实现HBase数据的迁移和拷贝。这种方式比较常用,效率高,也是最为推荐的数据迁移方式。
  • HBase的snapshot其实就是一组metadata信息的集合(文件列表),通过这些metadata信息的集合,就能将表的数据回滚到snapshot那个时刻的数据。
    • 首先我们要了解一下所谓的HBase的LSM类型的系统结构,我们知道在HBase中,数据是先写入到Memstore中,当Memstore中的数据达到一定条件,就会flush到HDFS中,形成HFile,后面就不允许原地修改或者删除了。
    • 如果要更新或者删除的话,只能追加写入新文件。既然数据写入以后就不会在发生原地修改或者删除,这就是snapshot做文章的地方。做snapshot的时候,只需要给快照表对应的所有文件创建好指针(元数据集合),恢复的时候只需要根据这些指针找到对应的文件进行恢复就Ok。这是原理的最简单的描述,下图是描述快照时候的简单流程:

 

2.3 快照实战

  • 1、创建表的snapshot

snapshot 'tableName', 'snapshotName'

  • 2、查看snapshot

list_snapshots  

 查找以test开头的snapshot

list_snapshots 'test.*'

  • 3、恢复snapshot

需要先对表进行disable操作,先把表置为不可用状态,然后在进行restore_snapshot的操作

disable 'tableName'
restore_snapshot 'snapshotName'
enable 'tableName'

  • 4、删除snapshot

delete_snapshot 'snapshotName'

  • 5、迁移 snapshot

  hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
  -snapshot snapshotName  \
  -copy-from hdfs://src-hbase-root-dir/hbase \
  -copy-to hdfs://dst-hbase-root-dir/hbase \
  -mappers 1 \
  -bandwidth 1024
  
  例如:
  hbase org.apache.hadoop.hbase.snapshot.ExportSnapshot \
  -snapshot test  \
  -copy-from hdfs://node01:8020/hbase \
  -copy-to hdfs://node01:8020/hbase1 \
  -mappers 1 \
  -bandwidth 1024

 注意:这种方式用于将快照表迁移到另外一个集群的时候使用,使用MR进行数据的拷贝,速度很快,使用的时候记得设置好bandwidth参数,以免由于网络打满导致的线上业务故障。

  • 6、将snapshot使用bulkload的方式导入

  hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles \
  hdfs://dst-hbase-root-dir/hbase/archive/datapath/tablename/filename \
  tablename
  
  例如:
  创建一个新表
  create 'newTest','f1','f2'
  hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles hdfs://node01:8020/hbase1/archive/data/default/test/6325fabb429bf45c5dcbbe672225f1fb newTest

3. HBase二级索引

 

  • HBase表后期按照rowkey查询性能是最高的。rowkey就相当于hbase表的一级索引
  • 但是在实际的工作中,我们做的查询基本上都是按照一定的条件进行查找,无法事先知道满足这些条件的rowkey是什么
  • 正常是可以通过hbase过滤器去实现。但是效率非常低,这是由于查询的过程中需要在底层进行大量的文件扫描。
  • HBase的二级索引
    • 为了HBase的数据查询更高效、适应更多的场景,诸如使用非rowkey字段检索也能做到秒级响应,或者支持各个字段进行模糊查询和多字段组合查询等, 因此需要在HBase上面构建二级索引, 以满足现实中更复杂多样的业务需求。
    • hbase的二级索引本质就是建立HBase表中列与行键之间的映射关系
    • 以空间换时间,提高查询的效率

 

  • 构建hbase二级索引方案
    • MapReduce方案
    • Hbase Coprocessor(协处理器)方案
    • Solr+hbase方案
    • ES+hbase方案
    • Phoenix+hbase方案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值