RDD 和 SparkSQL 运行时的区别
RDD
的运行流程
-
大致运行步骤
-
先将
RDD
解析为由Stage
组成的DAG
, 后将Stage
转为Task
直接运行
问题
-
任务会按照代码所示运行, 依赖开发者的优化, 开发者的会在很大程度上影响运行效率
解决办法
-
创建一个组件, 帮助开发者修改和优化代码, 但是这在
RDD
上是无法实现的SparkSQL
提供了什么?和
RDD
不同,SparkSQL
的Dataset
和SQL
并不是直接生成计划交给集群执行, 而是经过了一个叫做Catalyst
的优化器, 这个优化器能够自动帮助开发者优化代码也就是说, 在
SparkSQL
中, 开发者的代码即使不够优化, 也会被优化为相对较好的形式去执行Catalyst为了解决过多依赖
Hive
的问题,SparkSQL
使用了一个新的SQL
优化器替代Hive
中的优化器, 这个优化器就是Catalyst
, 整个SparkSQL
的架构大致如下-
API
层简单的说就是Spark
会通过一些API
接受SQL
语句 -
收到
SQL
语句以后, 将其交给Catalyst
,Catalyst
负责解析SQL
, 生成执行计划等 -
Catalyst
的输出应该是RDD
的执行计划 -
最终交由集群运行
总结
SparkSQL
和RDD
不同的主要点是在于其所操作的数据是结构化的, 提供了对数据更强的感知和分析能力, 能够对代码进行更深层的优化, 而这种能力是由一个叫做Catalyst
的优化器所提供的Catalyst
的主要运作原理是分为三步, 先对SQL
或者Dataset
的代码解析, 生成逻辑计划, 后对逻辑计划进行优化, 再生成物理计划, 最后生成代码到集群中以RDD
的形式运行Dataset 的特点
目标
-
理解
Dataset
是什么 -
理解
Dataset
的特性
Dataset
是什么?@Test def dataset1(): Unit = { // 1. 创建 SparkSession val spark = new sql.SparkSession.Builder() .master("local[6]") .appName("dataset1") .getOrCreate() // 2. 导入隐式转换 import spark.implicits._ // 3. 演示 val sourceRDD = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), Person("lisi", 15))) val dataset = sourceRDD.toDS() // Dataset 支持强类型的 API dataset.filter( item => item.age > 10 ).show() // Dataset 支持弱类型 API dataset.filter( 'age > 10 ).show() dataset.filter( $"age" > 10 ).show() // Dataset 可以直接编写 SQL 表达式 dataset.filter("age > 10").show() }
问题1:
People
是什么?People
是一个强类型的类问题2: 这个
Dataset
中是结构化的数据吗?非常明显是的, 因为
People
对象中有结构信息, 例如字段名和字段类型问题3: 这个
Dataset
能够使用类似SQL
这样声明式结构化查询语句的形式来查询吗?当然可以, 已经演示过了
问题4:
Dataset
是什么?Dataset
是一个强类型, 并且类型安全的数据容器, 并且提供了结构化查询API
和类似RDD
一样的命令式API
Dataset
的底层是什么?@Test def dataset2(): Unit = { // 1. 创建 SparkSession val spark = new sql.SparkSession.Builder() .master("local[6]") .appName("dataset1") .getOrCreate() // 2. 导入隐式转换 import spark.implicits._ // 3. 演示 val sourceRDD = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), Person("lisi", 15))) val dataset = sourceRDD.toDS() // dataset.explain(true) // 无论Dataset中放置的是什么类型的对象, 最终执行计划中的RDD上都是 InternalRow val executionRdd: RDD[InternalRow] = dataset.queryExecution.toRdd }
可以获取
Dataset
对应的RDD
表示总结
-
Dataset
是一个新的Spark
组件, 其底层还是RDD
-
Dataset
提供了访问对象中某个特定字段的能力, 不用像RDD
一样每次都要针对整个对象做操作 -
Dataset
和RDD
不同, 如果想把Dataset[T]
转为RDD[T]
, 则需要对Dataset
底层的InternalRow
做转换, 是一个比价重量级的操作
DataFrame 的作用和常见操作
目标
-
理解
DataFrame
是什么 -
理解
DataFrame
的常见操作
@Test def dataframe1(): Unit = { // 1. 创建 SparkSession val spark = SparkSession.builder() .appName("dataframe1") .master("local[6]") .getOrCreate() // 2. 创建 DataFrame import spark.implicits._ val dataFrame: DataFrame = Seq(Person("zhangsan", 15), Person("lisi", 20)).toDF() // 3. 看看 DataFrame 可以玩出什么花样 // select name from ... t where t.age > 10 dataFrame.where('age > 10) .select('name) .show() }
DataFrame如何创建:
@Test def dataframe2(): Unit = { val spark = SparkSession.builder() .appName("dataframe1") .master("local[6]") .getOrCreate() import spark.implicits._ val personList = Seq(Person("zhangsan", 15), Person("lisi", 20)) // 1. toDF val df1 = personList.toDF() val df2 = spark.sparkContext.parallelize(personList).toDF() // 2. createDataFrame val df3 = spark.createDataFrame(personList) // 3. read val df4 = spark.read.csv("dataset/BeijingPM20100101_20151231_noheader.csv") df4.show() }
DataFrame介绍:
@Test def dataframe3(): Unit = { // 1. 创建 SparkSession val spark = SparkSession.builder() .master("local[6]") .appName("pm analysis") .getOrCreate() import spark.implicits._ // 2. 读取数据集 val sourceDF: DataFrame = spark.read .option("header", value = true)//把第一行弄成header .csv("dataset/BeijingPM20100101_20151231.csv") // 查看 DataFrame 的 Schema 信息, 要意识到 DataFrame 中是有结构信息的, 叫做 Schema sourceDF.printSchema() // 3. 处理 // 1. 选择列 // 2. 过滤掉 NA 的 PM 记录 // 3. 分组 select year, month, count(PM_Dongsi) from ... where PM_Dongsi != NA group by year, month // 4. 聚合 // 4. 得出结论 // sourceDF.select('year, 'month, 'PM_Dongsi) // .where('PM_Dongsi =!= "NA") // .groupBy('year, 'month) // .count() // .show() // 是否能直接使用 SQL 语句进行查询 // 1. 将 DataFrame 注册为临表 sourceDF.createOrReplaceTempView("pm") // 2. 执行查询 val resultDF = spark.sql("select year, month, count(PM_Dongsi) from pm where PM_Dongsi != 'NA' group by year, month") resultDF.show() spark.stop() }
Dataset 和 DataFrame 的异同
目标
-
理解
Dataset
和DataFrame
之间的关系
DataFrame
就是Dataset
DataFrame
和Dataset
所表达的语义不同
@Test def dataframe4(): Unit = { val spark = SparkSession.builder() .appName("dataframe1") .master("local[6]") .getOrCreate() import spark.implicits._ val personList = Seq(Person("zhangsan", 15), Person("lisi", 20)) // DataFrame 是弱类型的 val df: DataFrame = personList.toDF() df.map( (row: Row) => Row(row.get(0), row.getAs[Int](1) * 2) )(RowEncoder.apply(df.schema)) .show() // DataFrame 所代表的弱类型操作是编译时不安全 // df.groupBy("name, school") // Dataset 是强类型的 val ds: Dataset[Person] = personList.toDS() ds.map( (person: Person) => Person(person.name, person.age * 2) ) .show() // Dataset 所代表的操作, 是类型安全的, 编译时安全的 // ds.filter( person => person.school ) }
Row
@Test def row(): Unit = { // 1. Row 如何创建, 它是什么 // row 对象必须配合 Schema 对象才会有 列名 val p = Person("zhangsan", 15) val row = Row("zhangsan", 15) // 2. 如何从 Row 中获取数据 row.getString(0) row.getInt(1) // 3. Row 也是样例类 row match { case Row(name, age) => println(name, age) } }
-
-