Spark中的RDD、DataFrame和DataSet讲解

Spark SQL入门:

1、Spark SQL的由来

2、Spark SQL的特性

3、Spark SQL的功能入口

4、Spark SQL与Hive集成方式

5、Spark SQL的开发以及使用方式

首先讲一下Spark SQL的前世今生,Spark SQL并不是Spark一开始就推出的,最早使用的是Hadoop自己的HIve查询引擎。

Hive的诞生,主要是因为开发MapReduce程序对Java的要求比较高,而很多数据分析师往往并没有很好的Java基础,为了让他们能够操作HDFS上的数据,推出了Hive。Hive与他们熟悉的RDBMS的SQL模型比较类似,容易掌握,Hive中的查询语言叫Hive SQL(HQL)。Hive底层是将SQL语句解析成MapsReduce程序执行,实质上还是运行的MapReduce程序。Hive的主要作用是能够让数据分析人员,以及数据开发人员方便的使用Hive进行数据仓库的建设。,使用SQL对数据仓库中的数据进行统计和分析。Hive的主要缺陷在于它的底层是基于MapReduce的,MapReduce程序的执行往往比较慢。

在Spark 0.x版的时候推出了Shark,Shark与Hive是紧密关联的,Shark底层很多东西还是依赖于HIve,但是修改了内存管理、物理计划、执行三个模块,底层使用Spark的基于内存的计算模型,性能上比Hive提升了很多倍。

在Spark 1.x的时候Shark被淘汰。在2014 年7月1日的Spark Summit 上,Databricks宣布终止对Shark的开发,将重点放到Spark SQL 上。Spark SQL 将涵盖Shark的所有特性,用户可以从Shark0.9 进行无缝的升级。

Databricks 推广的Shark相关项目一共有两个,分别是Spark SQL 和新的Hive on Spark 。

至于终止Shark的原因,Databricks表示,Shark更多是对Hive的改造,替换了Hive的物理执行引擎,因此会有一个很快的速度。然而,不容忽视的是,Shark继承了大量的Hive代码,因此给优化和维护带来了大量的麻烦。此后就重点放在Spark SQL的开发上,下Spark SQL不仅仅针对Hive中的数据,可以支持其他很多数据源。从性能上来说Shark比Hive要高出数倍,而Spark SQL比Hive又有了数倍的提升。下图是Spark SQL的发展时间线。

比较一下Spark SQL和Apache Hive

2、Spark SQL的特性

Spark SQL有如下特点:

1、支持多种数据源,Hive、RDD、Parquet、Json、JDBC等

2、多种性能优化技术

3、组件扩展性,对于SQL的语法解析器、分析器以及优化器,用户可以自己重新开发,且可以动态扩展。

从下面的图中可以更直观的看出

3、Spark SQL的功能入口

在之前Spark-core的时候我们知道,每个Spark-core应用程序都有一个功能入口SparkContext。同样的在Spark SQL中也有一个功能入口,在Spark 1.x的时候Spark SQL的入口有两个,SQLContext和HiveCOntext,两者的关系见下图:

SqlContext:主要负责对MySql这样的RDBMS的数据访问,还有可以对json parque文件的处理 HiveContext(是SqlContext的子类):主要负责读取Hive的元数据,处理Hive的数据 主要目的:让Spark应用可以读取hive的元数据,从而可以读取Hive中的表数据 在Spark2.x的时候:

在Spark 2.0之后,官方放弃了1.x时候的SQLContext和HiveContext的时候这样的容易让人困惑的两个入口,统一使用SparkSession作为Spark SQL的功能入口,用来创建DataFrame/DataSet。

SparkSession的构建方式有两种

// 方式一、通过SparkSession构建
    val spark = SparkSession
      .builder()
      .master("local[2]")
      .appName("Spark SQL")
      // 如果要读取Hive中的表,必须使用这个
      // 如果确定不使用Hive中的数据,建议不用开启,启动的时候会去连接Hive
      .enableHiveSupport()  
      .getOrCreate()
    
    // 方式二、通过SparkConf构建
    // 建议通过方式二构建,SparkConf不仅Spark SQL可以使用,其他的Spark模块也可以使用
    val conf = new SparkConf()
      .setAppName("Spark SQL")
      .setMaster("local[2]")
    
    val spark = SparkSession
      .builder()
      .config(conf)
      .enableHiveSupport()
      .getOrCreate()

4、Spark SQL与Hive集成方式

步骤:

  1. Spark集成hive,就是spark编译的时候给定了hive的相关参数;如果是idea环境, 需要在项目中集成sparksql的相关jar文件,也就是在pom.xml中配置spark-sql的依 赖
  2. 将hive-site.xml文件添加到spark的项目的classpath环境变量中 直接将hive-site.xml拷贝到spark安装的目录下的conf中 需要在hive-site.xml在配置以下参数 a.连接mysql数据库的url 用户名 密码 b.必须配置metastore,同时先启动 完成SparkSQL和Hive的集成
  3. 进入${Hive_HOME},执行拷贝
  4. 启动Hive的metasore服务(先启动hdfs的相关进程),注意这个是一个单例的进程, 不能重复启动,在启动一个新的服务前必须将原来的先停止
<property>
<name>hive.metastore.uris</name>
<value>thrift://hostname:9083</value>
<description>Thrift URI for the remote metastore. Used by metastore
client to connect to remote metastore.</description>
</property>

5、Spark SQL的开发以及使用方式

Spark SQL的开发方式主要有两种: 1.SQL(标准SQL + HiveQL)

val sqlStr = "select empno, xxx from emp sssss"
val resultDF = sqlContext.sql(sqlStr)
resultDF.write.table()\jdbc()....

2.DSL

链式编程

val df = sqlContext.read.table()\json()\parquet()\jdbc()...
val resultDF = df.select("").group().aggr().sortBy()
resultDF.write.table()\jdbc()....

使用Spark SQL的方式主要有四种:

第一种:spark-shell命令

spark.sql("select col1,col2 from a where col3 ==
xx").registerTmpTable("b").sql("select * from b")
  1. 使用DSL语句:倾向于RDD的使用方式,就是将sql的关键字封装成函数(算子)
df.select("col1","col2").where("col3"=="xx").group().agg()

第二种:spark-sql

shell命令行,参考bin/hive,可以直接写sql或者hql

第三种:spark-thirft 也就是 JDBC方式 shell命令行 参考hive中的bin/beeline

  1. 需要在hive-site.xml在配置以下参数
<property>
<name>hive.server2.thrift.bind.host</name>
<value>[hostname]</value>
<description>Bind host on which to run the HiveServer2 Thrift
service.</description>
</property>
  1. 拷贝到${SPARK_HOME}/conf/
  2. 在${SPARK_HOME}/启动spark的thrift服务,注意不是hive的
# 启动
$ sbin/start-thriftserver.sh
# 停止
$ sbin/stop-thriftserver.sh

第四种:IDEA开发工具 参考第一种

本文作为Spark SQL的第二部分,主要讲述一下RDD、DataFrame/DataSet之间的关系及相互转换。

主要从以下几个方面进行阐述:

1、Spark中的模块

2、什么是DataFrame

3、RDD和DataFrame的区别

4、什么是DataSet

5、RDD和Dataset的区别

6、Dataset和DataFrame的区别与联系

7、DataSet的创建

8、RDD转DataFrame原因及方式

9、DataFrame转RDD原因及方式

10、DataFrame转RDD的案例

1、Spark中的模块

上图展示了Spark的模块及各模块之间的关系,底层是Spark-core核心模块,Spark每个模块都有一个核心抽象,Spark-core的核心抽象是RDD,Spark SQL等都基于RDD封装了自己的抽象,在Spark SQL中是DataFrame/DataSet。相对来说RDD是更偏底层的抽象,DataFrame/DataSet是在其上做了一层封装,做了优化,使用起来更加方便。从功能上来说,DataFrame/DataSet能做的事情RDD都能做,RDD能做的事情DataFrame/DataSet不一定能做。

2、什么是DataFrame

在Spark中,DataFrame是一种以RDD为基础的分布式数据集,类似于传统数据库中的二维表格。

3、RDD和DataFrame的区别

DataFrame与RDD的主要区别在于,DataFrame带有schema元信息,即DataFrame所表示的二维表数据集的每一列都带有名称和类型。使得Spark SQL得以洞察更多的结构信息,从而对藏于DataFrame背后的数据源以及作用于DataFrame之上的变换进行了针对性的优化,最终达到大幅提升运行时效率的目标。

RDD,由于无从得知所存数据元素的具体内部结构,Spark Core只能在stage层面进行简单、通用的流水线优化。 DataFrame底层是以RDD为基础的分布式数据集,和RDD的主要区别的是:RDD中没有schema信息,而DataFrame中数据每一行都包含schema

DataFrame = RDD[Row] + shcema

4、什么是DataSet

Dataset是一个由特定领域的对象组成强类型(typedrel)集合,可以使用函数(DSL)或关系运算(SQL)进行并行的转换操作。 每个Dataset 还有一个称为“DataFrame”的无类型(untypedrel)视图,它是[[Row]]的数据集。

5、RDD和Dataset的区别

Dataset与RDD类似,但是,它们不使用Java序列化或Kryo,而是使用专用的Encoder编码器来序列化对象以便通过网络进行处理或传输。虽然Encoder编码器和标准序列化都负责将对象转换为字节,但Encoder编码器是动态生成的代码,并使用一种格式,允许Spark执行许多操作,如过滤,排序和散列,而无需将字节反序列化为对象

6、Dataset和DataFrame的区别与联系

区别:

  1. Dataset是强类型typedrel的,会在编译的时候进行类型检测;而DataFrame是弱类型untypedrel的,在执行的时候进行类型检测;
  2. Dataset是通过Encoder进行序列化,支持动态的生成代码,直接在bytes的层面进行排序,过滤等的操作;而DataFrame是采用可选的java的标准序列化或是kyro进行序列化

联系:

  1. 在spark2.x,DataFrame和Dataset的api进行了统一
  2. 在语法角度,DataFrame是Dataset中每一个元素为Row类型的特殊情况
    type DataFrame = Dataset[Row]
  3. DataFrame和Dataset实质上都是一个逻辑计划,并且是懒加载的,都包含着scahema信息,只有到数据要读取的时候,才会将逻辑计划进行分析和优化,并最终转化为RDD
  4. 二者由于api是统一的,所以都可以采用DSL和SQL方式进行开发,都可以通过sparksession对象进行创建或者是通过transform转化操作得到

7、DataSet的创建

DataSet的创建在上一篇文章《Spark SQL入门详解》中已经介绍。更多信息可以参见官方文档:

http://spark.apache.org/docs/2.2.1/sql-programming-guide.html#creating-datasets

8、RDD转DataFrame原因及方式

可以将RDD转成DataFrame之后,借用sparksql和sql以及HQL语句快速方便的使用sql语句统计和查询,比如说分组排名(row_number() over()) 分析函数和窗口函数去实现占比分析。

将RDD转化为DataFrame有两种方式:

方式一:通过反射推断schema 要求:RDD的元素类型必须是case class

方式二、编程指定schema 要求:RDD的元素类型必须是Row 自己编写schema(StructType) 调用SparkSession的createDatafrmame(RDD[Row],schema)

9、DataFrame转RDD原因及方式

  1. 解决一些使用sql难以处理的统计分析
  2. 将数据写入Mysql

a.DataFrame的write.jdbc,仅支持四种模式:append、overwrite、ignore、default

b.使用rdd的话,除了上述以外还支持insert 和 update操作,还支持数据库连接池 (自定 义,第三方:c3p0 hibernate mybatis)方式,批量高效将大量数据写入 Mysql

方式: DataFrame转换为RDD相对来说比较简单,只需要调用DataFrame的RDD算子即可。

10、DataFrame转RDD的案例

Java版本

package com.spark.sql;
​
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.rdd.RDD;
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.*;
import scala.Function1;
​
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
​
public class RddToDFjava {
    public static void main(String[] args) {
        SparkConf conf = new SparkConf()
                .setMaster("local[2]")
                .setAppName("RddToDF");
​
        SparkSession spark = SparkSession.builder()
                .config(conf)
                .getOrCreate();
​
        JavaRDD<String> dept =  spark.sparkContext().textFile("data/dept.txt", 1)
​
        JavaRDD<DeptBean> deptRDD = dept.map(line -> {
            String[] arr = line.split("\t");
            DeptBean deptBean = new DeptBean();
            int deptid;
            if (!arr[0].isEmpty()) {
                deptid = Integer.valueOf(arr[0]);
            } else {
                deptid = 0;
            }
            deptBean.setDeptid(deptid);
            deptBean.setDetname(arr[1]);
            deptBean.setAdd(arr[2]);
            return deptBean;
        });
​
        Dataset<Row> deptDF = spark.createDataFrame(deptRDD, DeptBean.class);
​
        deptDF.printSchema();
        deptDF.show();

    }
}

Scala版本

package com.spark.sql
​
import org.apache.spark.rdd.RDD
import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, Row, SparkSession}
import org.apache.spark.sql.types.{FloatType, IntegerType, StructField, StructType,StringType}
​
object Rdd2DataFrame {
  def main(args: Array[String]): Unit = {
​
    val conf = new SparkConf()
      .setMaster("local[2]")
      .setAppName(getClass.getSimpleName)
​
    // 创建SparkSession
    val spark = SparkSession
      .builder()
      .config(conf)
//      .enableHiveSupport()
      .getOrCreate()
​
    // 导入隐式转换包
    import spark.implicits._
​
    val deptLine: RDD[String] = spark.sparkContext.textFile("data/dept.txt")
​
    ​case class Dept(deptid:Int, deptname:String, addr:String)
   
    val dept: RDD[Dept] = deptLine.map(line => {
      val arr: Array[String] = line.split("\t")
      val deptid = arr(0).toInt
      val deptname = arr(1)
      val add = arr(2)
      Dept(deptid,deptname,add)
    })

    val deptDF: DataFrame = dept.toDF()
    deptDF.show()
​
  }
​
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值