Hbase–海量数据导入Hbase和Hbase数据导入Hdfs
文章目录
一:海量数据导入Hbase
1.代码实现
package hbasePut;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.client.Mutation;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.TestMiniMRClientCluster.MyReducer;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
public class HdfstrueHbase {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
System.setProperty("HADOOP_USER_NAME", "hadoop");
//加载配置文件
Configuration conf=new Configuration();
//将core.site hdfs.site放在src下
//设置高可用的集群
conf.set("fs.defaultFS", "hdfs://bd1906/");
//指定zookeeper的访问路径
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
//启动一个job
Job job=Job.getInstance(conf);
/**
* 指定job的主要入口
* class的获取
* 1:类名,class
* 2:对象,getClass
* 3.Class.forname
*/
job.setJarByClass(HdfstrueHbase.class);
//指定job中mapper对应的类
job.setMapperClass(MyMapper.class);
//指定reduce对应的类
//job.setReducerClass(MyReducer.class);
/**
* 泛型:只在代码编译时后生效,代码运行的时候自动删除
* 框架---map---reduce---hadfs
*/
//指定map输出的key value的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//指定reduce输出的key value的类型
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Put.class);
//指定输入
Path inpath=new Path("/user/hbase/ratings.dat");
FileInputFormat.addInputPath(job, inpath);
//指定输出路径 最终结果存储 值得hdfs的路径,输出的路径一定不能存在
//借助一个工具类指定表名
/**
* 参数1:表名
* 参数2:reduce的对应类
* 参数三:job
*/
TableMapReduceUtil.initTableReducerJob("user_hdfs", MyReducer.class, job,
null, null, null, null, false);
//真正的提交job,不会打印日志
//job.submit()
job.waitForCompletion(true);
}
/**
* key:行键
* value:其他数据信息
*
*/
static class MyMapper extends Mapper<LongWritable, Text, Text, Text>{
Text mk=new Text();
Text mv=new Text();
int i = 0;
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
//1::1193::5::978300760
i++;
String line = value.toString();
String[] datas = line.split("::");
mk.set(datas[0]+i);
mv.set(datas[1]+"\t"+datas[2]+"\t"+datas[3]);
context.write(mk, mv);
}
}
/**
* 泛型1:泛型2:map端输入
* 泛型3:reduce端输出的key
*泛型4:省略,mutation对象---put|delete
*
*解析map发送的数据进行封装
*/
static class MyReducer extends TableReducer<Text, Text, NullWritable>{
@Override
protected void reduce(Text key, Iterable<Text> value,Context context)
throws IOException, InterruptedException {
for (Text v : value) {
String[] datas = v.toString().split("\t");
Put p=new Put(key.getBytes());
//1::1193::5::978300760
p.addColumn("info".getBytes(), "version".getBytes(), datas[0].getBytes());
p.addColumn("info".getBytes(), "amount".getBytes(), datas[1].getBytes());
p.addColumn("info".getBytes(), "id".getBytes(), datas[2].getBytes());
//将put对象写出到hbase中
context.write(NullWritable.get(), p);
}
}
}
}
2.遇到的错误以及注意要点
1)这里使用的hdfs配置的是高可用集群,需要将linux中Hbase的安装配置文件中的core.site和hdfs.site放在src下,不然不能识别;
2)出现错误:Exception in thread “main” java.lang.IllegalArgumentException: Pathname /D:/bigdata_soft/hbase-1.2.6/lib/hbase-prefix-tree-1.2.6.jar from hdfs://bd1906/D:/bigdata_soft/hbase-1.2.6/lib/hbase-prefix-tree-1.2.6.jar is not a valid DFS filename.这个是jar包冲突导致的错误,解决办法:
- 调整jar包的导入顺序: hadoop导入在前, hbase 导入在后
- 代码TableMapReduceUtil.initTableReducerJob(“user_hdfs”, MyReducer.class,job,null,null,null,null,false);
3)TableMapReduceUtil工具类的initTableReducerJob()方法可以用来指定路径,和解决jar包冲突,是一个好东西。
4)如果使用TableMapReduceUtil指定了路径,前面的job指定的路径就可以删掉了
5)一定要注意路径问题,如果在hdfs中路径错误,可能在map阶段处理的时候回造成数组下表越界的问题
二:数据从Hbase导入到Hdfs
1.代码实现
这里是一个例题:求相同年龄的人数,将结果写到hdfs上面
package HbaseTsxt;
import java.io.IOException;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class HbaseToHdfs {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
System.setProperty("HADOOP_USER_NAME", "hadoop");
Configuration conf=HBaseConfiguration.create();
conf.set("fs.defaultFS", "hdfs://bd1906/");//mapreduce相关的,设置为高可用
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");//指定zookeeper
//Connection connection=ConnectionFactory.createConnection(conf);
//启动一个工作
Job job=Job.getInstance(conf);
//指定工作的入口
job.setJarByClass(HbaseToHdfs.class);
//指定reduce的入口
job.setReducerClass(MyReduce.class);
//指定map的入口
job.setMapperClass(MyMapper.class);
//指定map输出的key,value
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定reduce输出的key,value
//job.setOutputKeyClass(Text.class);
//job.setOutputValueClass(IntWritable.class);
//指定hbase的表
Scan scan=new Scan();
//scan.addColumn(family, qualifier);
TableMapReduceUtil.initTableMapperJob("HbaseAPI:HbaseAPI01", scan, MyMapper.class, Text.class, IntWritable.class, job,false);
//指定输出
FileSystem fs=FileSystem.get(conf);
Path output=new Path("/user/hbase/data/hbasetohdfs");
if(fs.exists(output)){
fs.delete(output, true);
}
//FileOutputFormat指定工作的输出路径
FileOutputFormat.setOutputPath(job, output);
//提交工作
job.waitForCompletion(true);
}
/**
* 泛型1:输出的key的类型
* 泛型2:输出的value的类型
*
*/
static class MyMapper extends TableMapper<Text, IntWritable>{
/**
* 参数1:行键对象
* 参数2:行键那一行的结果集
*/
//创建输入输出对象
Text mk=new Text();
IntWritable mv=new IntWritable();
@Override
protected void map(ImmutableBytesWritable key, Result value,
Mapper<ImmutableBytesWritable, Result, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
//listCells()方法取出所有的单元格
List<Cell> Cells = value.listCells();
//遍历单元格,做k,v处理
for (Cell cell : Cells) {
String qualifier=Bytes.toString(CellUtil.cloneQualifier(cell));
if (qualifier.equals("age")) {
String k=Bytes.toString(CellUtil.cloneValue(cell));
mk.set(k);
mv.set(1);
context.write(mk, mv);
}
}
}
}
/**
* 参数1:输入key的类型
* 参数2:输入value的类型
* 参数2:输出key的类型
* 参数4:输出value的类型
*/
static class MyReduce extends Reducer<Text, IntWritable, Text, IntWritable>{
IntWritable mv=new IntWritable();
@Override
/**
* 参数1:输入的key
* 参数2:map端的value集合
* key相同的启动一个reducetask,对values处理,v累=累加
*/
protected void reduce(Text key, Iterable<IntWritable> values,
Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
int count=0;
for (IntWritable v : values) {
count+=v.get();
}
mv.set(count);
context.write(key, mv);
}
}
}
2.遇到的错误以及注意要点
1)写出的路径存在导致的错误:Exception in thread “main” org.apache.hadoop.mapred.FileAlreadyExistsException: Output directory hdfs://bd1906/user/hbase/data/hbasetohdfs already exists 解决办法:每次指定输出到hdfs上面的时候进行判断,如果路径存在就删除路径下的文件
if(fs.exists(output)){
fs.delete(output, true);
}
2)权限问题,导致无法连接,可以加上权限管理
System.setProperty("HADOOP_USER_NAME", "hadoop");