1. HBase协处理器
- 官网文档
- 起源:
- Hbase 作为列族数据库的缺点:
- 无法轻易建立“二级索引”
- 难以执行求和、计数、排序等操作。
- 虽然 HBase 在数据存储层中集成了 MapReduce,能够有效用于数据表的分布式计算。然而在很多情况下,做一些简单的相加或者聚合计算的时候, 如果直接将计算过程放置在 server端,能够减少通讯开销,从而获得很好的性能提升。
- 于是, HBase 在 0.92 之后引入了协处理器(coprocessors),实现一些新特性:能够轻易建立二次索引、复杂过滤器(谓词下推)以及访问控制等。
- Hbase 作为列族数据库的缺点:
1.1 两种协处理器
- 协处理器有两种:observer和endpoint
- 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 这种协处理器的原理:
- Endpoint协处理器类似传统数据库中的存储过程,客户端可以调用这些 Endpoint 协处理器执行一段 Server 端代码,并将 Server 端代码的结果返回给客户端进一步处理
- 最常见的用法就是进行聚集操作。
- 如果没有协处理器,当用户需要找出一张表中的最大数据,即max 聚合操作,就必须进行全表扫描,在客户端代码内遍历扫描结果,并执行求最大值的操作。
- 这样的方法无法利用底层集群的并发能力,而将所有计算都集中到 Client 端统一执行,势必效率低下。
- 利用 Coprocessor,用户可以将求最大值的代码部署到 HBase Server 端,HBase将利用底层cluster 的多个节点并发执行求最大值的操作。即在每个 Region范围内执行求最大值的代码,将每个 Region 的最大值在 Region Server 端计算出,仅仅将该max值返回给客户端。
- 在客户端进一步将多个Region的最大值进一步处理而找到其中的最大值。这样整体的执行效率就会提高很多
- 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
- 通过修改 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
- 两种方式:①通过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.3 协处理器Observer实战
xdfsdfsdf
- 通过协处理器Observer实现向hbase当中一张表插入数据时,将数据复制一份保存到另外一张表当中去;但是只取第一张表当中的部分列数据,保存到第二张表当中去
- 打开hbase shell
cd /install/hbase-1.2.0-cdh5.14.2/
bin/hbase shell
- 在HBase当中创建一张表,表名proc1,并只有一个列族info
hbase(main):053:0> create 'proc1','info'
- 创建第二张表proc2,作为目标表
- 将第一张表当中插入数据的部分列,使用协处理器,复制到proc2表当中来
hbase(main):054:0> create 'proc2','info'
- 创建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();
}
}
- 将我们的协处理器打成一个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
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'
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
- 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方案