大数据实战二十四课 - Spark SQL04

第一章:上次课回顾

第二章:Spark SQL Functions

第三章:Catalog

第四章:DataSet

第五章:窗口函数

第六章:穿插Shuffle

第一章:上次课回顾

https://blog.csdn.net/zhikanjiani/article/details/96722109

上次课主要讲了DataFrame,它是一个分布式数据集,把数据转换成一个带名字的column,好比就是一张普通数据库中的表,它底层也做了一些优化;

对比RDD,RDD中看不到具体信息,DataFrame中可以看到具体信息。API一定要掌握。
PK哥公司都是做平台的,用户在界面输入东西而在数据平台底层都是使用API拼接的。
不就是用DataFrame写一个select把那几个字段拼接,用户的操作对于数据平台底层是用API编程。

表示方式最简单的比如竖线"|",需要使用"||"转义出来;外部数据源可以去处理不同文件系统上面不同格式的数据,让我们很方便的把多种数据源的东西弄进来,parquet的时候还有分区探测,还有schema的合并,分区探测的base在哪里;ORC、JDBC外部数据源的源码定义出来,测试把普通文本作用上schema后变成了一个DataFrame。

学长10个问题,只回答出来3个问题?就拿到了17.5k的工作。
小文件的解决方案
外部数据源的实现:谁创建谁,谁干了什么事
资源的分配调度

第二章:Spark SQL Functions

所有东西都在这个类中:functions.scala,是spark sql包下的。
在这里插入图片描述
如果真正掌握了Hive,学习Spark SQL,Impala会更清楚哪块没有学习。

如何在IDEA中设置快捷键和eclipse相同:file --> settings --> keymap
在这里插入图片描述
Ctrl + 0键,functions中有一个column方法,col方法;

/**
   * Returns a [[Column]] based on the given column name.
   *
   * @group normal_funcs
   * @since 1.3.0
   */
  def col(colName: String): Column = Column(colName)

  /**
   * Returns a [[Column]] based on the given column name. Alias of [[col]].
   *
   * @group normal_funcs
   * @since 1.3.0
   */
  def column(colName: String): Column = Column(colName)

联想到Hive中subString和subStr,其实是相同的两个东西;

  • 继续查看方法,asc,dsc,avg平均数;问题,collect_list和collect_set的区别,

  • collect_list方法的描述:Aggregate functions:returns a list of objects with duplicate.(返回具有重复项的对象列表)

  • collect_set方法描述:Aggregate functions:returns a set of objects with duplicate elements eliminated. (返回一组消除了重复元素的对象)

countDistinct:UV
broadcast:
coalesce
abs:
floor:地板函数,往下取
datediff:两个时间差
N多函数:

2.1 简单小应用

需求一:

读入在spark-core02中写的LogApp中的片段,日志还是自己造的日志,展示前10条。

package sparksql04

import org.apache.spark.sql.SparkSession

object FunctionsApp {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().master("local[2]").appName("FunctionApp").getOrCreate()

    import spark.implicits._

    val lines = spark.sparkContext.textFile("C:///Users//Administrator//Desktop//a.log")

    val df = lines.map(x => {
      val splits = x.split("\t")
      val length = splits.length
      var traffic = 0L
      if (length == 5) {
        val domain = splits(0) // index from zero
        (domain, 1L)
      } else {
        ("-", 0L)
      }
    }).toDF("domain", "times")

    df.show(10)

    spark.stop()
  }

}

输出信息如下,已经转换成了一个DF

+---------------+-----+
|         domain|times|
+---------------+-----+
|  24.168.127.82|    1|
| 222.153.127.24|    1|
| 105.77.222.228|    1|
|153.127.118.192|    1|
| 228.32.222.127|    1|
| 127.168.105.77|    1|
|118.105.127.127|    1|
| 127.228.127.82|    1|
| 127.168.105.31|    1|
| 127.24.222.127|    1|
+---------------+-----+
only showing top 10 rows

需求二

.........
 }).toDF("domain", "times")


    //分组以后做一个agg,按照times字段做一个聚合函数,取别名是pv
    df.groupBy("domain").agg(count("times").as("pv"))
      .select("domain","pv").show(false)

    spark.stop()
  }

输出:
+---------------+---+
|domain         |pv |
+---------------+---+
|156.127.32.222 |1  |
|127.24.222.127 |1  |
|31.127.24.153  |1  |
|118.105.156.168|1  |
|127.168.105.77 |1  |
|153.31.127.156 |1  |
|32.228.118.156 |1  |
|228.82.24.153  |1  |
|192.82.127.31  |1  |
|153.24.156.228 |1  |
|105.82.192.77  |1  |
|31.127.24.118  |1  |
|10.127.228.168 |1  |
|105.127.118.10 |1  |
|127.168.105.31 |2  |
|127.156.168.31 |1  |
|118.31.127.127 |1  |
|24.127.228.10  |1  |
|192.118.31.127 |1  |
|156.31.105.82  |1  |
+---------------+---+
only showing top 20 rows

自定义函数的编写:

  1. 定义函数
  2. 注册函数
  3. 使用函数

前提:数据准备:sparksql04下new一个file:

名字和爱好之间以tab键分割,求每个人喜欢的人的个数??

老二  17er,jeff,小朋友2滴
血狼  蝶舞,大树,老梁
若老  17er

测试数据能不能读进来:

object FunctionsApp{
		def main(args: Array[String]) : Unit = {
					val spark = SparkSession.builder().master("local[2]").appName("FunctionsApp")

					val info = spark.sparkContext.textFile("file:///G:/ruozedata_workspace/g7-spark/src/main/scala/sparksql04/functions.data")

					info.foreach(println)
					
					spark.stop()
	}

}

输出:发现是能够完整打印的
若老  17er
老二  17er,jeff,小朋友2滴
血狼  蝶舞,大树,老梁

开始进行处理数据:

  • 需要使用到的是trim 翻译:为指定的字符串列从两端修剪空格。
package sparksql04

import org.apache.spark.sql.SparkSession

object FunctionsApp {
  def main(args: Array[String]): Unit = {
      val spark = SparkSession.builder()
                                .master("local[2]")
                                .appName("FunctionsApp")
                                .getOrCreate()

        import spark.implicits._

        val info = spark.sparkContext.textFile("file:///C:/Users/Administrator/Desktop/testInfo.txt")

        val df = info.map(_.split("\t")).map(x => Info(x(0).trim, x(1).trim)).toDF

        df.show(false)

    spark.stop()
  }

      case class Info(name:String,likes:String)
}

输出如下:
+----+-------------------+
|name|likes              |
+----+-------------------+
|老二|17er,jeff,小朋友2滴|
|血狼|蝶舞,大树,老梁     |
|若老|17er               |
+----+-------------------+

坑:在sparksql04下面创建的file,copy file到spark.sparkContext.textFile中去的时候,idea运行的时候运行不出来,还是更换路径为本地路径。

2.2 Spark SQL自定义函数

需求:统计每个人喜欢的人数是几个?

package sparksql04

import org.apache.spark.sql.SparkSession

object FunctionsApp {
  def main(args: Array[String]): Unit = {
      val spark = SparkSession.builder()
                                .master("local[2]")
                                .appName("FunctionsApp")
                                .getOrCreate()

        import spark.implicits._

        val info = spark.sparkContext.textFile("file:///C:/Users/Administrator/Desktop/testInfo.txt")

        val df = info.map(_.split("\t")).map(x => Info(x(0).trim, x(1).trim)).toDF


        spark.udf.register("likes_num",(str:String) => {
          str.split(",").size
        })

        df.createOrReplaceTempView("info")
        spark.sql("select name,likes,likes_num(likes) as cnt from info").show(false)


    spark.stop()
  }

      case class Info(name:String,likes:String)
}


输出:
+----+-------------------+---+
|name|likes              |cnt|
+----+-------------------+---+
|老二|17er,jeff,小朋友2滴|3  |
|血狼|蝶舞,大树,老梁     |3  |
|若老|17er               |1  |
+----+-------------------+---+

小结: spark.udf.register(“likes_num”,(str:String) => {
str.split(",").size
})
不用SQL,用API的方式给它整出来。

第三章:Catalog

源代码包:catalog.scala 是在spark2.0以后才有的

回顾在Hive中,元数据是在mysql中,我们可以通过外部数据源去查询;
启动spark-shell,在2.0中,不需要使用jdbc的代码去查,可以直接使用catalog来访问。

  1. Catalog interface for Spark. To access this, use SparkSession.catalog.

  2. Returns the current default database in this session.

How to Use??

//select a,b,c from XXX
spark.catalog
写一个SQL语句,这只是一个字符串,一个SQL进来变成抽象语法树,在catalog这一步的时候就能直接判断

Spark shell中测试:

1、val catalog = spark.catalog			已经连到meta信息了

2、catalog.listDatabases().show		显示元数据中的所有数据库

3、catalog.listTables("default").show
使用catalog。listTables("ruoze_d6").show			//同hive-site.xml中的配置信息

4、catalog.listTables("default").select("name").show
list返回的就是DataSet

5、查看里面所有的函数信息
catalog.listFunctions().show()

第四章:DataSet

SchemaRDD ==> DataFrame ⇒ DataSet(1.6版本出来的)
compile-time type safety further optimization(编译时的类型安全优化)

走一个例子:

数据准备:cd /home/hadoop/data下面准备一份sales.csv的文件:

transformationsId,cunstomerId,itemId,amountId

在IDEA中写代码:

   spark.read.format("csv").option("header","true").option("inferSchema","true")
        .load("file:///home/hadoop/data/sales.csv")
        .show()

重点:

1、val df = spark.read.format(“csv”).option(“header”,“true”).option(“inferSchema”,“true”)
.load(“file:///home/hadoop/data/sales.csv”).show

会出来一份数据。

2、此时定义一个case class:
case class Sales(transactionId:Int,customerId:Int,itemId:Int,amountPaid:Double)

DataFrame怎么转换为DataSet?

val ds = spark.read.format(“csv”).option(“header”,“true”).option(“inferSchema”,“true”)
.load(“file:///home/hadoop/data/sales.csv”).as[Sales]

去到sparkshell中测试:测试df的时候打印出结构就是DataFrame:
把ds拷贝进去,

如何看DataFrame和DataSet的区别:

df.select("transactionId").show()
ds.select("transactionId").show()

两者的值是一样的,看不出区别:

概述:

  • A Dataset is a distributed collection of data. Dataset is a new interface added in Spark 1.6 that provides the benefits of RDDs (Strong typing, ability to use powerful lambda functions)

  • 解释:Strong typing(强类型),Datasets明确知道是什么类型,DataFrames不知道
    在这里插入图片描述
    小结:对于上图:Datasets是第一个反映过来是否语法错误,编译错误的;而SQL是最后反应过来的,会去yarn上申请资源,SQL过去,语法错误,咣当挂了。

第五章:窗口函数

零基础班中:窗口函数:

  • 求得product_id,product_name,product_status,area,click_count,rank,day

在Spark SQL中是如何实现的呢?

原先的操作
两 MySQL ==导入到Sqoop ===> Hive join hive
sqoop是关系型数据库到hadoop之间的导入导出

现在的操作
直接通过:
val cityDF = spark.read.format(“jdbs”).option…load()
val productDF = spark.read.format(“jdbs”).option…load()
val userClickDF = spark.table("")

如何做join:
cityDF join productDF join userClickDF

有一个比较麻烦的事情:
{“product_status”:1} ==> 自定义一个UDF函数把1取出来,怎么定义udf呢?

另一种udf的写法:
val xxxUdf = udf(() => {})

窗口又是怎么回事情呢?

  • row_number().over(Window.partitionBy("").orderBy("")).as("")

  • 最终的结果写到MySQL中去,外部数据源JDBC的写不要用,有问题,自己测试跑两次跑三次

DF/DS的数据输出到DataBase的最佳实践:

那我们应该如何写出去,使用foreach,df.foreachPartition( x =>{
scalike
})

零基础班针对hadoop来上的,高级班针对spark的整套环境学习

引出概念:

幂等性: 先删后加:每跑一次作业都需要把前一次的作业删除

第六章:穿插Shuffle

问题:map的输出非常多;map和reduce的缓冲区都需要加大。

社区提出的解决方案:
spark.shuffle.consolidateFiles = true 这个参数做一些合并操作

一个core上连续执行在图中表示那几个??

好处:在map输出端合并,省去很多磁盘IO.

极端问题:Reduce个数非常多怎么办,也是一样会崩溃的。
在这里插入图片描述

进入到spark.apache.org/docs/1.5.1/configuraton.html

找到参数:
spark.shuffle.consolidateFiles,这个参数设置为true可以提升很多性能

后来官方更改为sort了,后期就使用sort了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值