Spark读取Hbase数据转DataFrame(Java API)

Spark读取Hbase数据转DataFrame(Java API)

最近刚开始学习Spark,没接触过Scala,发现网上Java的资料比Scala少很多,一边学习一边实现功能很多遇到的问题列在最后做个笔记。初学者,有理解错误还请指正。
 

环境

Hadoop2.7.7+Hbase1.3.5+Spark2.2.0
项目Maven管理
完整代码在github:https://github.com/823319298/SparkUse
帮到您的话可以去打个星!
 

过程

pom文件引入相应依赖

HBase依赖:

		<dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.3.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.3.5</version>
        </dependency>

Spark项目之后会用到SparkSql,一起引入了:

		<dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.11</artifactId>
            <version>2.2.0</version>
        </dependency>

先列出所有需要的包,大家可以导入的时候参考

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
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.TableInputFormat;
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
import org.apache.hadoop.hbase.util.Base64;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;
import scala.Tuple2;

import java.io.IOException;
import java.util.*;

 

初始化Spark:

分为本地环境与集群环境,自己做相应修改

	//本地环境
	SparkConf sparkConf = new SparkConf().setAppName("SparkSql countMon").setMaster("local[*]");
	//修改序列化方式
	sparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer");
	//SparkSession替代JavaSparkContext
	SparkSession sc = SparkSession.builder().config(sparkConf).getOrCreate();

SparkSession是spark新增的替代了JavaSparkContext和弃用的SQLContext 成为新的入口。序列化的配置是必需的,在网络间传输的时候对象需要序列化。
如果需要从SparkSession获取到JavaSparkContext:

		JavaSparkContext.fromSparkContext(sc.sparkContext())

初始化Scan,需要对Scan进行Base64编码,Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法。

		//Scan.set这里可以加一些scan条件,读hbase
		//将scan编码
        ClientProtos.Scan proto = ProtobufUtil.toScan(scan);
        String scanToString = Base64.encodeBytes(proto.toByteArray());

初始化Hbase,配置HBaseConfiguration,初始化Scan

		Configuration hbConf = HBaseConfiguration.create();

        //指定输入表名
        hbConf.set(TableInputFormat.INPUT_TABLE, "你的表名字");
        //Base-64编码的Scanner
        hbConf.set(TableInputFormat.SCAN,scanToString);

至此初始化准备工作完成。
 

读取Hbase

		//获取JavaSparkContext
		JavaSparkContext jsc = JavaSparkContext.fromSparkContext(sc.sparkContext());
		//newAPIHadoopRDD这个RDD用于读取存储在Hadoop中的数据,文件有新 API 输入格式和额外的配置选项,传递到输入格式
		//参数conf会被广播,不可以修改,所以最好一个RDD一个conf
		JavaPairRDD<ImmutableBytesWritable, Result> hbaseRDD = jsc.newAPIHadoopRDD(hbConf,TableInputFormat.class, ImmutableBytesWritable.class, Result.class);
        //使用map函数将JavaPairRDD=>JavaRDD,反之使用mapToPair函数
        JavaRDD<Row> dataRDD = hbaseRDD.map(new Function<Tuple2<ImmutableBytesWritable,Result>,Row>() {
        	//序列化表示UID,用于类的版本控制
            private static final long serialVersionUID = 1L;
            //重写call()函数,返回Row类型,这部分需要根据自己需求将result中的数据封装为Object[]
            @Override
            public Row call(Tuple2<ImmutableBytesWritable, Result> tuple2) {
                Result result = tuple2._2;
                String[] values = new String[1441];
                values[0] = Bytes.toString(result.getRow());
                int sum=1;
                for(int i=0;i<24;++i){
                    for(int j=0;j<60&&sum<1441;++j){
                        values[sum] = Bytes.toString(result.getValue("A".getBytes(),(String.format("%02d",i)+String.format("%02d",j)).getBytes()));
                        ++sum;
                    }
                }
                //creat()方法参数(object... value)
                return RowFactory.create((Object[]) values);
            }
        });

现在我们已经得到了JavaRDD(Row),这个RDD里有我们封装好的hbase里的数据,下面需要将它转化为DataFrame。他们之间的关系可以看下面遇到的问题里面的三者关系。

        List<StructField> structFields= new ArrayList<>();
        //创建StructField,基本等同于list内有几个StructField就是几列,需要和上面的Row的Object[]对应
        structFields.add(DataTypes.createStructField("rowKey", DataTypes.StringType, true));
        int sum=1;
        for(int i=0;i<24;++i){
            for(int j=0;j<60&&sum<1441;++j){
            	structFields.add(DataTypes.createStructField("R"+String.format("%02d",i)+String.format("%02d",j), DataTypes.StringType, true));
                 ++sum;
            }
        }
        //构建schema,可以把它理解为架子,结构
        StructType schema = DataTypes.createStructType(structFields);
        //生成DataFrame,把书放入架子,把数据放入结构里就变成了dataframe
        Dataset<Row> HBaseDF = sc.createDataFrame(dataRDD, schema);

想看一下架子长啥样:

		HBaseDF.printSchema();

最后得到的dataframe就是HbaseDF
下面是遇到的一些问题和一些注意事项,有需要的可以看看,完整代码在github:https://github.com/823319298/SparkUse
 

遇到的问题

 

版本问题,报错:

Exception in thread "main" java.lang.NoSuchMethodError: org.apache.hadoop.conf.Configuration.getPassword(Ljava/lang/String;)[C

之前Spark用的2.4.5,降到了2.2.0
 

ImmutableBytesWritable类型

Hbase一般使用ImmutableBytesWritable作为rowkey的类型
 

Tuple2

元组的概念
 

lambda表达式

接口中只有一个需要被实现的方法时才能使用
Function接口:

package org.apache.spark.api.java.function;

import java.io.Serializable;

@FunctionalInterface
public interface Function<T1, R> extends Serializable {
    R call(T1 var1) throws Exception;
}

流标识符,用于类的版本定义,不定义也会自动计算出一个。定义为1L的话类修改后反序列化不会报错,向上兼容。

private static final long serialVersionUID = 1L;

Function实现了Serializable接口,因此可以被保存到文件中或者网络传输
 

RDD和DataFrame和DataSet三者间的区别

这里看一下这个https://blog.csdn.net/weixin_43087634/article/details/84398036 感谢大佬
Garbage Collection可以翻译为“垃圾收集” – 一般主观上会认为做法是:找到垃圾,然后把垃圾扔掉。在VM中,GC的实现过程恰恰相反,GC的目的是为了追踪所有正在使用的对象,并且将剩余的对象标记为垃圾,随后标记为垃圾的对象会被清除,回收这些垃圾对象占据的内存,从而实现内存的自动管理。
Dataframe是Dataset的特列,DataFrame=Dataset[Row]

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值