Hbase的JavaAPI
一、java操作Hbase步骤
java操作Hbase步骤:
- 1、添加依赖
- 2、获取HbaseConfiguration对象
- 3、设置zookeeper地址
- 4、获取连接对象
- 5、获取操作对象
- 6、操作
- 7、释放资源
二、添加依赖
<!--若要使用org.apache.hadoop.hbase.client的API,需要加上:-->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.2.5</version>
</dependency>
<!--若要使用org.apache.hadoop.hbase.mapreduce的API,需要加上:-->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>2.2.5</version>
</dependency>
三、获取连接
public static void main(String[] args) throws Exception {
//获取HBaseConfiguration对象
Configuration conf = HBaseConfiguration.create();
// 设置zk的地址
conf.set("hbase.zookeeper.quorum" , "linux1:2181,linux2:2181,linux3:2181");
// 获取连接对象
Connection conn = ConnectionFactory.createConnection(conf);
// DDL tools 运维 管理员对象
Admin admin = conn.getAdmin();
// 列出系统中所有的表
TableName[] tableNames = admin.listTableNames();
for (TableName tableName : tableNames) {
String name = tableName.getNameAsString();
System.out.println("当前系统中的表有: "+ name);
}
// 操作数据 [表]
// conn.getTable()
//释放资源
admin.close();
conn.close();
}
核心操作对象有两个:
- Admin
conn.getAdmin()获取
操作命名空间、建表、删除表、查看表列表 … split flush等 - Table
conn.getTable()获取
操作和数据相关的,DML一些命令
四、新增、删除命名空间
public static void main(String[] args) throws Exception {
//获取Hbase的配置文件对象
Configuration conf = HBaseConfiguration.create();
//配置zk集群地址
conf.set("hbase.zookeeper.quorum", "doit01:2181,doit02:2181,doit03:2181");
//获取连接对象
Connection conn = ConnectionFactory.createConnection(conf);
//获取Admin对象用来操作表
Admin admin = conn.getAdmin();
//获取命名空间
//admin.getNamespaceDescriptor(namespace);
//创建命名空间构造器
Builder test_java = NamespaceDescriptor.create("test_java");
//设置命名空间属性
test_java.addConfiguration("VERSION", "2");
//构建命名空间描述器
NamespaceDescriptor build3 = test_java.build();
//创建命名空间
admin.createNamespace(build3);
// 删除名称空间
// admin.deleteNamespace("test_java");
//释放资源
admin.close();
conn.close();
}
创建步骤:
- 创建命名空间构造器
- 用命名空间构造器设置命名空间属性
- 用命名空间构造器构造命名空间描述器
- 用命名空间描述器创建命名空间
删除步骤:
- admin.deleteNamespace(“命名空间名”)
注意:命名空间创建构建器的方法与表、列族创建构造器方法有所不同
五、新增表
public static void main(String[] args) throws Exception {
//获取Hbase的配置文件对象
Configuration conf = HBaseConfiguration.create();
//配置zk集群地址
conf.set("hbase.zookeeper.quorum", "doit01:2181,doit02:2181,doit03:2181");
//获取连接对象
Connection conn = ConnectionFactory.createConnection(conf);
//获取Admin对象用来操作表
Admin admin = conn.getAdmin();
//创建表的构建器
TableDescriptorBuilder table_java = TableDescriptorBuilder
.newBuilder(TableName.valueOf("test_java:table_java"));
//创建列族构建器
ColumnFamilyDescriptorBuilder info = ColumnFamilyDescriptorBuilder
.newBuilder("info".getBytes());
ColumnFamilyDescriptorBuilder other = ColumnFamilyDescriptorBuilder
.newBuilder("other".getBytes());
//设置列族属性 最大版本
info.setMaxVersions(4);
//构建列族描述器
ColumnFamilyDescriptor build = info.build();
ColumnFamilyDescriptor build1 = other.build();
//往表的构建器中加列族描述器
List<ColumnFamilyDescriptor> bulids = Arrays.asList(build, build1);
//往表的构建器中设置列族
table_java.setColumnFamilies(bulids);
//构建表描述器
TableDescriptor table = table_java.build();
//设置预分区,会被分为[0,5),[5,10),[10,10之后的所有数据)三个分区
byte[][] bytes = new byte[][]{"5".getBytes(), "10".getBytes()};
//创建表
admin.createTable(table, bytes);
//释放资源
admin.close();
conn.close();
}
步骤:
- 创建表构建器
- 创建列族构建器
- 用列族构建器设置列族属性
- 用列族构建器构建列族描述器
- 将列族描述器添加到表构建器
- 用表构建器构建表描述器
- 用表描述器去创建表,在此之前可以设置表的预分区
注意:设置表的预分区的属性是个byte的二维数组,因为设置表的预分区需要指定每个分区的起始row_key,而row_key在存储时本身就是一个byte数组。
设置表的预分区可以防止大量请求涌入一台服务器
创建表时,至少要有一个列族
五、修改表
public static void main(String[] args) throws IOException {
//获取Hbase的配置文件对象
Configuration conf = HBaseConfiguration.create();
//配置zk集群地址
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
//获取连接对象
Connection coon = ConnectionFactory.createConnection(conf);
//获取admin对象操作表
Admin admin = coon.getAdmin();
//添加列族
//创建列族构造器
// ColumnFamilyDescriptorBuilder builder = ColumnFamilyDescriptorBuilder
// .newBuilder("cf1".getBytes());
// ColumnFamilyDescriptorBuilder builder1 = ColumnFamilyDescriptorBuilder
// .newBuilder("cf2".getBytes());
// //添加列族属性
// builder.setMaxVersions(3);
// //构建列族
// ColumnFamilyDescriptor cf1 = builder.build();
// ColumnFamilyDescriptor cf2 = builder1.build();
// //admin添加列族
// admin.addColumnFamily(TableName.valueOf("test_java:table_java"),cf1);
// admin.addColumnFamily(TableName.valueOf("test_java:table_java"),cf2);
//修改列族
//创建列族构造器
ColumnFamilyDescriptorBuilder builder3 = ColumnFamilyDescriptorBuilder
.newBuilder("cf2".getBytes());
//添加列族属性
builder3.setMaxVersions(3);
//构建列族
ColumnFamilyDescriptor cf3 = builder3.build();
//admin修改列族
admin.modifyColumnFamily(TableName.valueOf("test_java:table_java"),cf3);
//删除列族
//admin.deleteColumnFamily(TableName.valueOf("test_java:table_java"),"cf1".getBytes());
admin.close();
coon.close();
}
- 添加列族:admin.addColumnFamily(TableName.valueOf(“test_java:table_java”),cf2);
- 修改列族:admin.modifyColumnFamily(TableName.valueOf(“test_java:table_java”),cf3);
修改列族时列族不存在会报错 - 删除列族:admin.deleteColumnFamily(TableName.valueOf(“test_java:table_java”),“cf1”.getBytes());
六、删除表
public static void main(String[] args) throws Exception{
//自己写了个Util类将前面获取连接的代码封装了起来
Admin admin = HbaseUtil.getAdmin();
// 获取表对象
TableName a = TableName.valueOf("a");
//判断表是否存在
if(admin.tableExists(a)){
// 禁用表
admin.disableTable(a);
// 判断表是否被禁用
if(admin.isTableDisabled(a)){
//是的话删除表
admin.deleteTable(a);
}else{
//不是的话禁用表
admin.disableTable(a);
if(admin.isTableDisabled(a)){
admin.deleteTable(a);
}
}
}else{
System.out.println("脑子有病 ,没有这张表");
}
//释放资源
admin.close();
}
删除表之前需要先禁用表
七、DML操作
1、增加一行或多行数据
public static void main(String[] args) throws IOException {
//获取配置文件对象
Configuration conf = HBaseConfiguration.create();
//连接zooleeper
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
//获取连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取表对象
Table table = conn.getTable(TableName.valueOf("test_java:table_java"));
//增加数据
//获取put对象,指定要增加数据的行
Put put1 = new Put("rk1003".getBytes());
//在put对象中增加单元格数据,指定该单元格数据的列族、列名、值
put1.addColumn("base_info".getBytes() , "name".getBytes() , "xxxliu".getBytes()) ;
put1.addColumn("base_info".getBytes() , "age".getBytes() , "1".getBytes()) ;
put1.addColumn("base_info".getBytes() , "gender".getBytes() , "M".getBytes()) ;
Put put2 = new Put("rk1004".getBytes());
put2.addColumn("base_info".getBytes() , "name".getBytes() , "xxxxliu".getBytes()) ;
put2.addColumn("base_info".getBytes() , "age".getBytes() , "1".getBytes()) ;
put2.addColumn("base_info".getBytes() , "gender".getBytes() , "F".getBytes()) ;
//添加多行
table.put(Arrays.asList(put1 , put2));
//释放资源
table.close();
conn.close();
}
2、删除一行或多行或一个单元格或多个单元格
public static void main(String[] args) throws IOException {
//获取配置文件对象
Configuration conf = HBaseConfiguration.create();
//连接zooleeper
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
//获取连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取表对象
Table table = conn.getTable(TableName.valueOf("test_java:table_java"));
//删除数据
//获取delete对象,指定要删除数据的行
Delete delete = new Delete("002".getBytes());
//在delete对象中增加要删除的单元格,指定该单元格的列族、列名
delete.addColumn("info".getBytes(),"name".getBytes());
delete.addColumn("other".getBytes(),"age".getBytes());
Delete delete1 = new Delete("001".getBytes());
delete1.addColumn("other".getBytes(),"age".getBytes());
//不增加要删除的单元格,则会一整行都被删除
Delete delete2 = new Delete("001".getBytes());
table.delete(Arrays.asList(delete , delete1 , delete2 ));
//释放资源
table.close();
conn.close();
}
3、获取一行或多行数据或一行的列族或单元格
public static void main(String[] args) throws IOException {
//获取配置文件对象
Configuration conf = HBaseConfiguration.create();
//连接zooleeper
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
//获取连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取表对象 库名:表名
Table table = conn.getTable(TableName.valueOf("test_java:table_java"));
//获取查询对象,获取指定行的数据(整行数据,多个单元格)
Get get = new Get("001".getBytes());
//添加查询条件,指定该行的某个列族
//get.addFamily("info".getBytes());
//添加查询条件,指定该行的某个单元格 列族、列名
//get.addColumn(Bytes.toBytes(family),Bytes.toBytes(qualifier));
//获取多行 传个list,list里面多个get对象
//Result[] results = table.get(Arrays.asList(get, get1));
//获取一行
Result result = table.get(get);
//判断是否有下一个单元格数据
while(result.advance()){
//获取单元格
Cell cell = result.current();
//获取该单元格的行键
byte[] bytes = CellUtil.cloneRow(cell);
//获取该单元格的列族
byte[] bytes0 = CellUtil.cloneFamily(cell);
//获取该单元格的列名
byte[] bytes1 = CellUtil.cloneQualifier(cell);
//获取该单元格的值
byte[] bytes2 = CellUtil.cloneValue(cell);
//注意这里是new String(bytes)
//bytes.toString和String.valueOf(bytes)得到的是个对象
System.out.println(new String(bytes)+"---"+
new String(bytes0)+"---"+
new String(bytes1)+"---"+
new String(bytes2)+"---");
}
//释放资源
table.close();
conn.close();
}
4、获取整张表数据
public static void main(String[] args) throws IOException {
//获取配置文件对象
Configuration conf = HBaseConfiguration.create();
//连接zooleeper
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
//获取连接
Connection conn = ConnectionFactory.createConnection(conf);
//获取表对象
Table table = conn.getTable(TableName.valueOf("test_java:table_java"));
Scan scan = new Scan();
//设置数据的起始行,也可不设置
scan.withStartRow(Bytes.toBytes("rk1001")) ;
// 设置数据展示的行数,也可不设置
scan.setLimit(2) ;
//获取到多行数据
ResultScanner scanner = table.getScanner(scan);
//获取行迭代器
Iterator<Result> iterator = scanner.iterator();
// 遍历每行
while(iterator.hasNext()){
// 获取当前行
Result result = iterator.next();
//之后的操作与之前过去一行数据的操作一致
// 获取行内的单元格数据
while(result.advance()){
//获取单元格
Cell cell = result.current();
//获取该单元格的行键
byte[] bytes = CellUtil.cloneRow(cell);
//获取该单元格的列族
byte[] bytes0 = CellUtil.cloneFamily(cell);
//获取该单元格的列名
byte[] bytes1 = CellUtil.cloneQualifier(cell);
//获取该单元格的值
byte[] bytes2 = CellUtil.cloneValue(cell);
//注意这里是new String(bytes)
//bytes.toString和String.valueOf(bytes)得到的是个对象
System.out.println(new String(bytes)+"---"+
new String(bytes0)+"---"+
new String(bytes1)+"---"+
new String(bytes2)+"---");
}
}
//释放资源
table.close();
conn.close();
}
5、利用缓存批量写入
public static void main(String[] args) throws Exception {
//获取配置文件对象
Configuration conf = HBaseConfiguration.create();
//连接zooleeper
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
//获取连接
Connection conn = ConnectionFactory.createConnection(conf);
// 批次操作对象
BufferedMutator mutator = conn.getBufferedMutator(TableName.valueOf("tb_stu"));
// 读取文本数据 导入到hbase中
BufferedReader br = new BufferedReader(new FileReader("d://user.txt"));
String line = null ;
List<Mutation> ls = new ArrayList<>() ;
while((line = br.readLine())!=null){
//u002,lss,22,F
String[] spls = line.split(",");
Put put = new Put(spls[0].getBytes());
put.addColumn("cf".getBytes() , "name".getBytes() , spls[1].getBytes());
put.addColumn("cf".getBytes() , "age".getBytes() , spls[2].getBytes());
put.addColumn("cf".getBytes() , "gender".getBytes() , spls[3].getBytes());
ls.add(put) ;
}
//读到内存缓冲
mutator.mutate(ls);
//刷写到磁盘
mutator.flush();
//关闭资源
br.close();
mutator.close();
conn.close();
}
mutator.flush()和mutator.close()都可以实现刷写到磁盘,只不过一个主动一个被动。
mutator.setWriteBufferPeriodicFlush 隔一段时间刷
6、使用importTsv工具上传数据
Importtsv是hbase自带的一个 csv文件(逗号分隔值文件格式)转HFile文件(Hbase存储文件格式)的工具,它能将csv文件转成HFile文件,并发送给regionserver
它的本质,是内置的一个将csv文件转成hfile文件的mr程序!
ImportTsv命令的参数说明如下:
-Dimporttsv.skip.bad.lines=false - 若遇到无效行则失败
-Dimporttsv.separator=, - 使用特定分隔符,默认是tab也就是\t
-Dimporttsv.timestamp=currentTimeAsLong - 使用导入时的时间戳
-Dimporttsv.mapper.class=my.Mapper - 使用用户自定义Mapper类替换TsvImporterMapper
-Dmapreduce.job.name=jobName - 对导入使用特定mapreduce作业名
-Dcreate.table=no - 避免创建表,注:如设为为no,目标表必须存在于HBase中
-Dno.strict=true - 忽略HBase表列族检查。默认为false
-Dimporttsv.bulk.output=/user/yarn/output 作业的输出目录
两条命令:
- CSV文件转HFILE文件,需要先将CSV文件传到HDFS中
hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator=, -Dimporttsv.columns='HBASE_ROW_KEY,info:name,info:age,other:gender' -Dimporttsv.bulk.output=/stu/output test:tb_stu /stu/input/user.txt
-Dimporttsv.separator=, 指定分隔符为逗号
-Dimporttsv.columns=‘HBASE_ROW_KEY,info:name,info:age,other:gender’ 分割出来的字段对应为表中字段,info、other为列族名
-Dimporttsv.bulk.output=/stu/output 输出目录
test:tb_stu 要对应的表,会自动创建,也可自己手动创建
/stu/input/user.txt 输入目录,即CSV文件所在目录
- HFile注入Hbase表中
hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles /stu/output/ test:tb_stu
/stu/output/ HFILE文件所在目录
test:tb_stu 要转换的表
八、行键设计与二级索引
行键和查询维度
排序、region定位、布隆过滤器、块索引定位都需用到行键,所以,对于一些经常性的查询维度一定要在行键上体现,以加快查询效率
rowkey设计原则
- 将最高频度的查询维度设计在rowkey上(方便查询)
- 定长(方便排序)
- 唯一性(防止数据覆盖)
- 不要太长(影响存储效率)
二级索引
HBase本身只提供基于行键和全表扫描的查询,而行键索引单一,对于多维度的查询困难(如:对于价格+天数+酒店+交通的多条件组合查询困难),全表扫描效率低下。此时就需要建立二级索引。
二级索引的本质就是建立各列值与行键之间的映射关系
二级索引设计思路
如图,当要对F:C1这列建立索引时,只需要建立F:C1各列值到其对应行键的映射关系,如C11->RK1等,这样就完成了对F:C1列值的二级索引的构建,当要查询符合F:C1=C11对应的F:C2的列值时(即根据C1=C11来查询C2的值,图1青色部分)
其查询步骤如下:
-
根据C1=C11到索引数据中查找其对应的RK,查询得到其对应的RK=RK1
-
得到RK1后就自然能根据RK1来查询C2的值了 这是构建二级索引大概思路,其他组合查询的联合索引的建立也类似。
九、MR整合Hbase
Hbase为mapreduce开发了TableInputFormat和TableOutputFormat,因此,在mapreduce程序中访问hbase数据是很轻松的。
1、读取普通文件中的数据存储到hbase表中
public class MapReduce {
static class MovieMapper extends Mapper<LongWritable, Text, Text, MovieBean> {
Text k = new Text();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
//将读取到的数据转化为bean
MovieBean movieBean = JSON.parseObject(line, MovieBean.class);
/*
* 设计行键,使用定长movieid与定长时间戳组合作为rowkey
*/
String movie = movieBean.getMovie();//1 2340
String timeStamp = movieBean.getTimeStamp();
String mid = StringUtils.leftPad(movie, 5, '0');
String ts = StringUtils.rightPad(timeStamp, 10, '0');
String rowKey = mid + "_" + ts;
k.set(rowKey);
context.write(k, movieBean);
}
}
static class MovieReducer extends TableReducer<Text, MovieBean, ImmutableBytesWritable> {
@Override
protected void reduce(Text key, Iterable<MovieBean> values, Context context)
throws IOException, InterruptedException {
String rk = key.toString();
//遍历每个movieBean,将其封装到put对象中
for (MovieBean mb : values) {
String movie = mb.getMovie();
double rate = mb.getRate();
String timeStamp = mb.getTimeStamp();
String uid = mb.getUid();
// 封装put对象
Put put = new Put(rk.getBytes());
put.addColumn("cf".getBytes(), "movie".getBytes(), Bytes.toBytes(movie));
put.addColumn("cf".getBytes(), "rate".getBytes(), Bytes.toBytes(rate));
put.addColumn("cf".getBytes(), "timeStamp".getBytes(), Bytes.toBytes(timeStamp));
put.addColumn("cf".getBytes(), "uid".getBytes(), Bytes.toBytes(uid));
context.write(null, put);
}
}
}
public static void main(String[] args)
throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = HBaseConfiguration.create();
//连接zookeeper
conf.set("hbase.zookeeper.quorum", "linux1:2181,linux2:2181,linux3:2181");
Job job = Job.getInstance(conf);
///设置Mapper对应类
job.setMapperClass(MovieMapper.class);
//设置map输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(MovieBean.class);
//设置文件输入路径
FileInputFormat.setInputPaths(job, new Path("D:\\多易教育\\Hbase\\movie"));
//设置输出为表
TableMapReduceUtil.initTableReducerJob("movie", MovieReducer.class, job);
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
- Mapper中读取数据并设计行键为key
- Reducer继承TableReducer,并且输出类型改为ImmutableBytesWritable,在Reducer中将每行数据封装为put对象并写出,写出时输出key为null,输出value为每行数据的put对象
- job中不用再设置最终输出key、value类型,也不用设置Reducer类。设置输出路径改为设置输出为表
//设置输出为表
TableMapReduceUtil.initTableReducerJob("movie", MovieReducer.class, job);
参数分别为表名、Reducer类和job对象
需要注意的是,此表必须在Hbase中已存在