篇幅中使用的HBase版本为1.1.2
HBase提供了一套Java API来支持Java程序对HBase数据库的请求操作,在hbase shell中能够使用的都可以通过这套API来实现
HBase有两套API,分别是1.0和2.0,在较新版本的HBase中使用1.0的API时,很多类和方法都被标记为Deprecated,官方表示旧版本的API将会在3.0版本中删除,所以推荐使用2.0
本篇中使用的API均为2.0
2.0官方API文档
- com.google.protobuf:rpc通信依赖
- org.apache.zookeeper:连接zk依赖
- hbase-client:hbase客户端
- hbase-common:hbase组件
使用maven可以简单方便地管理jar包,pom文件示例如下:
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<hbase.version>1.1.2hbase.version>
<zookeeper.version>3.4.5zookeeper.version>
properties>
<dependencis>
<dependency>
<groupId>com.google.protobufgroupId>
<artifactId>protobuf-javaartifactId>
<version>2.5.0version>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>${zookeeper.version}version>
dependency>
<dependency>
<groupId>org.apache.hbasegroupId>
<artifactId>hbase-clientartifactId>
<version>${hbase.version}version>
dependency>
<dependency>
<groupId>org.apache.hbasegroupId>
<artifactId>hbase-commonartifactId>
<version>${hbase.version}version>
dependency>
dependencis>
HBase API的使用可以归纳为一下几个步骤:
1.获得Configuration实例:其中保存了环境和配置信息
2.在Configuration中设置zk和master的相关信息,如果hbase的配置文件在环境变量中则不需要配置
3.获得Connection实例连接到zk
4.通过Connection实例获得Admin和Table实例调用其方法进行操作
其中Admin和Table为HBase API中提供的一个统一操作接口,在1.0中对应的是HAdmin和HTable
Admin对应的是DDL操作
Table对应的是相关表的DML操作
//1.获得Configuration实例并进行相关设置
Configuration configuration = HBaseConfiguration.create();
configuration.set("hbase.zookeeper.quorum", "localhost:2181");
configuration.set("hbase.master", "localhost:16010");
//2.获得Connection实例
Connection connection = ConnectionFactory.createConnection(configuration);
//3.1获得Admin接口
Admin admin = connection.getAdmin();
//3.2获得Table接口,需要传入表名
Table table = connection.getTable(tableName)
为了程序的可维护性和方便调用,这里将HBase API提供的接口划分成了三个类
1.HBaseInfo:保存了Configuration和Connection,并进行一些初始化的设置,如zk地址等
2.HBaseDDLUtil:继承HBaseInfo,通过父类的Configuration和Connection获得Admin实例并使用其提供的方式进行DDL操作
3.HBaseDMLUtil:继承HBaseInfo,通过父类的Configuration和Connection获得Table实例并使用其提供的方式进行DML操作
DDL操作
/**
* 创建表
* @param tableName 表名
* @param familyNames 列族名
* */
public static void createTable(String tableName, String... familyNames) throws IOException {
if (admin.tableExists(TableName.valueOf(tableName))) {
return;
}
//通过HTableDescriptor类来描述一个表,HColumnDescriptor描述一个列族
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
for (String familyName : familyNames) {
tableDescriptor.addFamily(new HColumnDescriptor(familyName));
}
admin.createTable(tableDescriptor);
}
/**
* 删除表
* @param tableName 表名
* */
public static void dropTable(String tableName) throws IOException {
//删除之前要将表disable
if (!admin.isTableDisabled(TableName.valueOf(tableName))) {
admin.disableTable(TableName.valueOf(tableName));
}
admin.deleteTable(TableName.valueOf(tableName));
}
DML操作
HBase中的CRUD都是通过对应的对象来操作的,例如:
Put为新增,如果记录已经存在会用新值覆盖,相当于修改
Delete为删除
Get为查询
/**
* 指定行/列中插入数据
* @param tableName 表名
* @param rowKey 主键rowkey
* @param family 列族
* @param column 列
* @param value 值
* TODO: 批量PUT
*/
public static void insert(String tableName, String rowKey, String family, String column, String value) throws IOException {
table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(family), Bytes.toBytes(column), Bytes.toBytes(value));
table.put(put);
}
/**
* 删除表中的指定行
* @param tableName 表名
* @param rowKey rowkey
* TODO: 批量删除
*/
public static void delete(String tableName, String rowKey) throws IOException {
table = connection.getTable(TableName.valueOf(tableName));
Delete delete = new Delete(Bytes.toBytes(rowKey));
table.delete(delete);
}
篇幅有限,HBase工具类的代码已上传至Github
API文档中还有许多实用的函数没有接触到,希望在实践中可以操作一下
要使用HBase的MapReduce API需要在pom文件中添加以下依赖:
<dependency>
<groupId>org.apache.hbasegroupId>
<artifactId>hbase-serverartifactId>
<version>${hbase.version}version>
dependency>
HBase实现了TableInputFormat和TableOutputFormat用于读写HBase表
TableMapper类和TableReducer类,在使用MapReduce操作HBase的时候可以借助这两个类从HBase中读数据和写数据
用于读取HBase表数据并生成键值对
将数据表按照Region分割成split,既有多少个Regions就有多个splits
然后将Region按行键分成
和普通的Mapper的区别在于
TableMapper将输入的
该类将Reducer的输出类型限制为Mutation,Mutation是HBase中Delete/Put/Get/Append类的父类,也就是说TableReducer将输出类型限制在这几个类之中
自定义的Reducer类继承TableReducer,指定其输入的
该类负责将Reducer的输出数据写入到HBase中
MyTableMapper
public class MyTableMapper extends TableMapper<ImmutableBytesWritable, ImmutableBytesWritable> {
ImmutableBytesWritable k = new ImmutableBytesWritable();
ImmutableBytesWritable v = new ImmutableBytesWritable();
/**
* 表中的每行都会调用一次map函数
* */
@Override
protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
//遍历改行中的结果集
for (Cell cell : value.rawCells()) {
//获得rowkey
byte[] row = CellUtil.cloneRow(cell);
//获得值
byte[] rowValue = CellUtil.cloneValue(cell);
k.set(row);
v.set(rowValue);
context.write(k, v);
}
}
}
MyTableReducer
public class MyTableReducer extends TableReducer<ImmutableBytesWritable, ImmutableBytesWritable, ImmutableBytesWritable> {
@Override
protected void reduce(ImmutableBytesWritable key, Iterable values, Context context) throws IOException, InterruptedException {
for (ImmutableBytesWritable value : values) {
Put put = new Put(key.get());
put.addColumn(Bytes.toBytes("f1"), Bytes.toBytes("c3"), value.get());
context.write(key, put);
}
}
}
驱动程序
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration configuration = HBaseConfiguration.create();
//设置读取的表
configuration.set(TableInputFormat.INPUT_TABLE, "client");
Job job = Job.getInstance(configuration, "hbase-mr-api");
//可以和普通mr程序一样进行设置
job.setJarByClass(Driver.class);
job.setInputFormatClass(TableInputFormat.class);
job.setMapperClass(MyTableMapper.class);
job.setMapOutputKeyClass(ImmutableBytesWritable.class);
job.setMapOutputValueClass(ImmutableBytesWritable.class);
//也可以使用hbase提供的工具类来设置job
TableMapReduceUtil.initTableReducerJob("t1", MyTableReducer.class, job);
job.waitForCompletion(true);
}
示例代码以上传至Github
在上面的代码中可以看到,设置job的方法有两种:MapReduce常用方式和HBase提供的TableMapReduceUtil工具类
两种方式的设置效果是相同的,通过普通的方式进行设置的时候需要配置TableInputFormat/TableOutputFormat的相关属性,如上的
//设置读取的表
configuration.set(TableInputFormat.INPUT_TABLE, "client");
而是使用TableMapReduceUtil的initTableMapperJob/initTableReducerJob则是通过参数传递这些配置,下面给出具体的配置项
TableInputFormat
TableOutputFormat
initTableMapperJob
initTableReducerJob
作者:@小黑