RDD转换DataFrame&SparkSql操作数据源&跨数据源join&SparkSql与DF和DS的比较&spark元数据:catalog

RDD转换DataFrame

方式1:使用反射来推断包含特定对象类型的RDD的模式

 def inferReflection(spark: SparkSession) ={
     
    val rdd = spark.sparkContext.textFile("D:\\ssc\\spark\\people.txt")
    //RDD => DF时需要的隐式转换
    import spark.implicits._
    val pDF = rdd.map(_.split(",")).map(line => People(line(0).toInt,line(1),line(2).toInt)).toDF()
    pDF.printSchema()
    pDF.show()
  }

通过反射class的这种方式可以获得schema创建DataFrame,简单通用,但是在创建外部数据源的场景下不适用

方式2:通过编程接口,StructType可以构造Schema,然后将其应用于现有的RDD

def program(spark: SparkSession) = {
     
    //创建RDD
    val rdd = spark.sparkContext.textFile("D:\\ssc\\spark\\people.txt")
    //1、RDD[String] => RDD[Row]
    val infoRDD: RDD[Row] = rdd.map(line => {
     
      val strArr = line.split(",")
      val id = strArr(0).trim.toInt
      val name = strArr(1).trim
      val age = strArr(2).trim.toInt
      Row(id, name, age)
    })
    //2、创建schema
    val schema: StructType = StructType(
      StructField("id", IntegerType, true) ::
        StructField("name", StringType, true) ::
        StructField("age", IntegerType, true) :: Nil
    )
    //3、包装DataFrame
    val df: DataFrame = spark.createDataFrame(infoRDD, schema)
    df.printSchema()
    df.show()
  }

步骤:

  • 在原RDD上创建RDD[Row]
  • 创建和上一步对应的行结构类型的StructType
  • 将schema和Rows结合,创建出DF

SQL操作数据源

每个数据源下都有一个sql选项卡,其中就是对应的SQL数据源,生成对应的SQL视图代码
官网数据源

CREATE TEMPORARY VIEW jsonTable
USING org.apache.spark.sql.json
OPTIONS (
  path "examples/src/main/resources/people.json"
)

SELECT * FROM jsonTable

跨数据源join
跨数据源join是Spark非常好用的一个特性,从不同的数据源拿到spark中,在从spark写出去
案例:从hive和mysql中分别拿出一个表join
1、jdbc

val jdbcDF = spark.read
      .format("jdbc")
      .option("url", "jdbc:mysql://ruozedata001:6619")
      .option("dbtable", "ruozedata.dept")
      .option("user", "root")
      .option("password", "mysqladminroot")
      .load()

2、hive
idea里面连接hive需要如下操作
1)启动hive的metastore:hive --service metastore -p 9083 &
2)把集群中的windows运行hive的配置文件放入到resources目录下
3、代码需要开启enableHiveSupport()

val spark = SparkSession.builder
      .master("local")
      .appName(this.getClass.getSimpleName)
      .enableHiveSupport()
      .getOrCreate()
val hiveDF = spark.sql("select * from default.emp")

注意windows下一定要放入如下参数,不然找不到block块

hdfs-site.xml
<property>
        <name>dfs.client.use.datanode.hostname</name>
        <value>true</value>
 </property>

4、join

val joinDF: DataFrame = jdbcDF.join(hiveDF, "deptno")
+------+----------+--------+-----+------+---------+----+----------+------+------+
|deptno|     dname|     loc|empno| ename|      job| mgr|  hiredate|   sal|  comm|
+------+----------+--------+-----+------+---------+----+----------+------+------+
|    10|ACCOUNTING|NEW YORK| 7934|MILLER|    CLERK|7782| 1982-1-23|1300.0|  null|
|    10|ACCOUNTING|NEW YORK| 7839|  KING|PRESIDENT|null|1981-11-17|5000.0|  null|
|    10|ACCOUNTING|NEW YORK| 7782| CLARK|  MANAGER|7839|  1981-6-9|2450.0|  null|
|    20|  RESEARCH|  DALLAS| 7902|  FORD|  ANALYST|7566| 1981-12-3|3000.0|  null|
|    20|  RESEARCH|  DALLAS| 7876| ADAMS|    CLERK|7788| 1987-5-23|1100.0|  null|
|    20|  RESEARCH|  DALLAS| 7788| SCOTT|  ANALYST|7566| 1987-4-19|3000.0|  null|
|    20|  RESEARCH|  DALLAS| 7566| JONES|  MANAGER|7839|  1981-4-2|2975.0|  null|
|    20|  RESEARCH|  DALLAS| 7369| SMITH|    CLERK|7902|1980-12-17| 800.0|  null|
|    30|     SALES| CHICAGO| 7900| JAMES|    CLERK|7698| 1981-12-3| 950.0|  null|
|    30|     SALES| CHICAGO| 7844|TURNER| SALESMAN|7698|  1981-9-8|1500.0|   0.0|
|    30|     SALES| CHICAGO| 7698| BLAKE|  MANAGER|7839|  1981-5-1|2850.0|  null|
|    30|     SALES| CHICAGO| 7654|MARTIN| SALESMAN|7698| 1981-9-28|1250.0|1400.0|
|    30|     SALES| CHICAGO| 7521|  WARD| SALESMAN|7698| 1981-2-22|1250.0| 500.0|
|    30|     SALES| CHICAGO| 7499| ALLEN| SALESMAN|7698| 1981-2-20|1600.0| 300.0|
+------+----------+--------+-----+------+---------+----+----------+------+------+

5、如果不需要全部的数据,下面我们将经过处理选择我们需要的数据

import spark.implicits._

val ds: Dataset[Result] = joinDF.select("empno","ename","deptno","dname","sal").as[Result]

ds.show()

+-----+------+------+----------+------+
|empno| ename|deptno|     dname|   sal|
+-----+------+------+----------+------+
| 7934|MILLER|    10|ACCOUNTING|1300.0|
| 7839|  KING|    10|ACCOUNTING|5000.0|
| 7782| CLARK|    10|ACCOUNTING|2450.0|
| 7902|  FORD|    20|  RESEARCH|3000.0|
| 7876| ADAMS|    20|  RESEARCH|1100.0|
| 7788| SCOTT|    20|  RESEARCH|3000.0|
| 7566| JONES|    20|  RESEARCH|2975.0|
| 7369| SMITH|    20|  RESEARCH| 800.0|
| 7900| JAMES|    30|     SALES| 950.0|
| 7844|TURNER|    30|     SALES|1500.0|
| 7698| BLAKE|    30|     SALES|2850.0|
| 7654|MARTIN|    30|     SALES|1250.0|
| 7521|  WARD|    30|     SALES|1250.0|
| 7499| ALLEN|    30|     SALES|1600.0|
+-----+------+------+----------+------+

6、保存数据

 ds.write.format("orc").save("./dataset/emp_result")

 ds.write.format("jdbc")
      .option("url", "jdbc:mysql://ruozedata001:6619")
      .option("dbtable", "ruozedata.emp_result")
      .option("user", "root")
      .option("password", "mysqladminroot").mode("overwrite").save()

SQL与DF与DS的比较
问题:spark.read.load() 这句代码没用指定读取格式,那么它的默认格式是什么?

现在我们需要对比的是SQL、DF、DS三者对Syntax Errors和Analysis Errors的不同程度的响应

在上一步中,我们将joinDF转化成了joinDS,现在我们就看看他们在选择需要的列的时候,做了什么样的执行计划

RDD转换DataFrame&SparkSql操作数据源&跨数据源join&SparkSql与DF和DS的比较&spark元数据:catalog_第1张图片

00 Project [empno#6]
01 +- Join Inner, (cast(deptno#0 as decimal(10,0)) = cast(deptno#13 as decimal(10,0)))
02    :- Project [deptno#0]
03    :  +- Filter isnotnull(deptno#0)
04    :     +- Relation[deptno#0,dname#1,loc#2] JDBCRelation(ruozedata.dept) [numPartitions=1]
05    +- Project [empno#6, deptno#13]
06       +- Filter isnotnull(deptno#13)
07          +- HiveTableRelation `ruozedata_hive`.`emp`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, [empno#6, ename#7, job#8, mgr#9, hiredate#10, sal#11, comm#12, deptno#13]
---------------------------------
00 SerializeFromObject [staticinvoke(class org.apache.spark.unsafe.types.UTF8String, StringType, fromString, input[0, java.lang.String, true], true, false) AS value#79]
01 +- MapElements com.ruozedata.bigdata.SourceJoinApp$$$Lambda$2060/1996620584@54997f67, class com.ruozedata.bigdata.SourceJoinApp$Result, [StructField(empno,StringType,true), StructField(ename,StringType,true), StructField(deptno,StringType,true), StructField(dname,StringType,true), StructField(sal,StringType,true)], obj#78: java.lang.String
02    +- DeserializeToObject newInstance(class com.ruozedata.bigdata.SourceJoinApp$Result), obj#77: com.ruozedata.bigdata.SourceJoinApp$Result
03       +- Project [empno#6, ename#7, deptno#0, dname#1, sal#11]
04          +- Join Inner, (cast(deptno#0 as decimal(10,0)) = cast(deptno#13 as decimal(10,0)))
05             :- Project [deptno#0, dname#1]
06             :  +- Filter isnotnull(deptno#0)
07             :     +- Relation[deptno#0,dname#1,loc#2] JDBCRelation(ruozedata.dept) [numPartitions=1]
08             +- Project [empno#6, ename#7, sal#11, deptno#13]
09                +- Filter isnotnull(deptno#13)
10                   +- HiveTableRelation `ruozedata_hive`.`emp`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, [empno#6, ename#7, job#8, mgr#9, hiredate#10, sal#11, comm#12, deptno#13]

由此我们可以根据在Spark SQL应用中,选择列的时候,SQL、DF、DS三者做一个比较

SQL DF DS
Syntax Errors runtime compile compile
Analysis Errors runtime runtime compile
  • 在执行SQL的时候,无论是语法错误还是运行错误,都无法在编译时就提前暴露出来
  • 在执行DF的时候,算子如果写错了,会提前暴露出来,但是写的列名只有在运行的时候才会检查是否正确
  • 在执行DS的时候,由于case class反射的机制,算子和列名都可以提前到代码编写时就检测到错误

所以选择顺序为 DS > DF > SQL

面试题:RDD、DS、DF的区别

  • RDD不支持SQL
  • DF每一行都是Row类型,不能直接访问字段,必须解析才行
  • DS每一行是什么类型是不一定的,在自定义了case class之后可以很自由的获得每一行的信息
  • DataFrame与Dataset均支持spark sql的操作,比如select,group by之类,还 能注册临时表/视窗,进行sql语句操作
  • Dataset在需要访问列中的某个字段时是非常方便的,然而,如果要写一些适配性很强的函数时,如果使用Dataset,行的类型又不确定,可能是各种case class,无法实现适配,这时候用DataFrame即Dataset[Row]就能比较好的解决问题。

面试题:RDD和Dataset/DataFrame中的Persist的默认缓存级别

  • Dataset中的缓存级别是 MEMORY_AND_DISK
    RDD转换DataFrame&SparkSql操作数据源&跨数据源join&SparkSql与DF和DS的比较&spark元数据:catalog_第2张图片
  • RDD中的缓存级别是 MEMORY_ONLY
    RDD转换DataFrame&SparkSql操作数据源&跨数据源join&SparkSql与DF和DS的比较&spark元数据:catalog_第3张图片

面试题:Spark RDD和Spark SQL的的cache有什么区别

  • Spark RDD的cache是lazy的,需要action才会执行cache操作
  • Spark SQL的cache是egaer的,马上就cache了

Spark元数据管理: catalog

	val spark = SparkSession.builder().master("local[2]").appName(this.getClass.getSimpleName).enableHiveSupport().getOrCreate()

	//拿到元数据
	val catalog: Catalog = spark.catalog
	//列举所有的database
    val dbList: Dataset[Database] = catalog.listDatabases()
    dbList.show(false)
    //当前database
    println("----->"+catalog.currentDatabase)

    import spark.implicits._
    //展示数据库的名字
    dbList.map(_.name).show()
    //设置数据库
    catalog.setCurrentDatabase("ruozedata_hive")
    //展示当前数据库中的表
    catalog.listTables().show(false)
    //过滤表
    val listTable = catalog.listTables()
    listTable.filter('name like  "ruozedata%").show(false)

    println("-------------缓存---------------------")

    //判断某个表是否缓存
    println(catalog.isCached("ruozedata_hive"))
    catalog.cacheTable("ruozedata_hive")
    println(catalog.isCached("ruozedata_hive"))
    catalog.uncacheTable("ruozedata_hive")

    //展示所有的function
    catalog.listFunctions().show(1000,false)

注意:catalog.cacheTable是lazy的方法

注册UDF函数,展示函数

spark.udf.register("udf_string_length",(word:String) => {
     
    word.split(",").length
})

catalog.listFunctions().filter('name === "udf_string_length").show(false)

你可能感兴趣的:(spark)