spark笔记-spark sql

1. SPARK SQL

是Spark套件中的一个模块,他将数据的计算任务通过SQL的形式转换成了RDD的计算,类似于Hive通过SQL的形式将数据的计算任务转换成了MapReduce。

1.1Spark SQL的特点

  1. 和Spark Core的无缝集成,我可以在写整个RDD应用的时候,配置Spark SQL来实现我的逻辑。
  2. 统一的数据访问方式,Spark SQL提供标准化的SQL查询。
  3. Hive的继承,Spark SQL通过内嵌Hive或者连接外部已经部署好的hive实例,实现了对Hive语法的继承和操作。
  4. 标准化的连接方式,Spark SQL可以通过启动thrift Server来支持JDBC、ODBC的访问,将自己作为一个BI Server使用。

老版本中有hivecontext 现在使用sparksession
spark sql例子:

val spark=SparkSession.builder().master("local").appName("hehe")
     .config("spark.some.config.option","some-value")
        .getOrCreate()
    import  spark.implicits._ //隐式转换
    val df = spark.read.json("hdfs://hadoop100:8020/spark/0917/wxf.json")
    df.show()
    # DSL式
    df.filter($"age">21).show()
    df.createGlobalTempView("perpson")
    spark.sql("SELECT * FROM persons where age > 21").show()
    spark.stop()

1.2 Spark SQL 数据抽象

  1. RDD(Spark1.0)-> DataFrame(Spark1.3)-> DataSet(Spark1.6)
  2. Spark SQL提供了DataFrame和DataSet的数据抽象。
  3. DataFrame就是RDD + Schema,可以认为是一张二维表格。他的劣势是在编译器不进行表格中的字段的类型检查。在运行期进行检查。
  4. DataSet是Spark最新的数据抽象,Spark的发展会逐步将DataSet作为主要的数据抽象,弱化RDD和DataFrame。DataSet包含了DataFrame所有的优化机制。除此之外提供了以样例类为Schema模型的强类型。
  5. DataFrame = DataSet[Row]
  6. DataFrame和DataSet都有可控的内存管理机制,所有数据都保存在非堆上,都使用了catalyst进行SQL的优化。

1.3 SQL在Spark的解析过程

spark sql解析过程

  • SQL 解析阶段 - SparkSqlParser
    从 Spark 2.0.0 版本开始引入了第三方语法解析器工具 ANTLR(详情参见 SPARK-12362),Antlr 是一款强大的语法生成器工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件,是当前 Java 语言中使用最为广泛的语法生成器工具,我们常见的大数据 SQL 解析都用到了这个工具,包括 Hive、Cassandra、Phoenix、Pig 以及 presto 等。目前最新版本的 Spark 使用的是 ANTLR4,通过这个对 SQL 进行词法分析并构建语法树。
    在 SQL 解析阶段生成了 Unresolved LogicalPlan,包含了 UnresolvedRelation 和 unresolvedalias 等对象。Unresolved LogicalPlan 仅仅是一种数据结构,不包含任何数据信息,比如不知道数据源、数据类型,不同的列来自于哪张表等.

  • 绑定逻辑计划阶段 - Analyzer
    Analyzer 阶段会使用事先定义好的 Rule 以及 SessionCatalog 等信息对 Unresolved LogicalPlan 进行 transform。SessionCatalog 主要用于各种函数资源信息和元数据信息(数据库、数据表、数据视图、数据分区与函数等)的统一管理。

  • 优化逻辑计划阶段 - Optimizer
    绑定逻辑计划阶段对 Unresolved LogicalPlan 进行相关 transform 操作得到了 Analyzed Logical Plan,这个 Analyzed Logical Plan 是可以直接转换成 Physical Plan 然后在 Spark 中执行。但是如果直接这么弄的话,得到的 Physical Plan 很可能不是最优的,因为在实际应用中,很多低效的写法会带来执行效率的问题,需要进一步对Analyzed Logical Plan 进行处理,得到更优的逻辑算子树。
    这个阶段的优化器主要是基于规则的(Rule-based Optimizer,简称 RBO),而绝大部分的规则都是启发式规则,也就是基于直观或经验而得出的规则,比如列裁剪(过滤掉查询不需要使用到的列)、谓词下推(将过滤尽可能地下沉到数据源端)、常量累加(比如 1 + 2 这种事先计算好) 以及常量替换(比如 SELECT * FROM table WHERE i = 5 AND j = i + 3 可以转换成 SELECT * FROM table WHERE i = 5 AND j = 8)等等。

  • 全阶段代码生成阶段 - WholeStageCodegen
    从逻辑计划生成物理计划(Physical Plan),但是这个物理计划还是不能直接交给 Spark 执行的,Spark 最后仍然会用一些 Rule 对 SparkPlan 进行处理,这个过程是 prepareForExecution 过程,这些 Rule 如下:

protected def preparations: Seq[Rule[SparkPlan]] = Seq(
   PlanSubqueries(sparkSession),                          //特殊子查询物理计划处理
   EnsureRequirements(sparkSession.sessionState.conf),    //确保执行计划分区与排序正确性
   CollapseCodegenStages(sparkSession.sessionState.conf), //代码生成
   ReuseExchange(sparkSession.sessionState.conf),         //节点重用
   ReuseSubquery(sparkSession.sessionState.conf))         //子查询重用

上面的 Rule 中 CollapseCodegenStages 是重头戏,这就是大家熟知的全代码阶段生成,Catalyst 全阶段代码生成的入口就是这个规则。当然,如果需要 Spark 进行全阶段代码生成,需要将 spark.sql.codegen.wholeStage 设置为 true(默认)。

2. RDD,DataFrame,DataSet关系

RDD弹性的体现:

  1. 存储的弹性:内存与磁盘的自动切换
  2. 容错的弹性:数据丢失 可以自动恢复
  3. 计算的弹性:计算出错重试机制
  4. 分片的弹性:根据需要重新分片

DataSet 包括RDD,DataFrame。

Dataframe与RDD相比:Dataframe比较高级

  1. 具有表结构信息的,即schema。
  2. DataFrame比RDD执行效率更高、减少数据读取以及执行计划的优化
  3. DataFrame比RDD性能更高:RDD存在于JVM上,可以非常精准的控制内存
    定制化内存管理
  4. 数据以二进制的方式存在于非堆内存,节省了大量空间之外,还摆脱了GC的限制。
  5. 查询计划通过Spark catalyst optimiser进行优化:可以是不太会使用RDD的工程师写出相对高效的代码

DataSet:分布式数据集
DataSet是强类型的,DataFrame=Dataset[Row],Dataframe是Dataset的特列 ROW实际是定长的字符串

细节:

show------dataframe的print
collect------rdd的print

2.1 DataFrame 使用方式

  1. DataFrame支持两种查询方式一种是DSL风格,另外一种是SQL风格。
  • DSL风格:
    1. 你需要引入 import spark.implicit._ 这个隐式转换,可以将DataFrame隐式转换成RDD。
  • SQL风格:
    1. 你需要将DataFrame注册成一张表格,如果你通过CreateTempView这种方式来创建,那么该表格Session有效,如果你通过CreateGlobalTempView来创建,那么该表格跨Session有效,但是SQL语句访问该表格的时候需要加上前缀 global_temp。
    2. 你需要通过sparkSession.sql 方法来运行你的SQL语句。

2.2 对于DataFrame Row对象的访问方式

1、DataFrame = DataSet[Row], DataFrame里面每一行都是Row对象
2、如果需要访问Row对象中的每一个元素,你可以通过下标 row(0);你也可以通过列名 row.getAs[String]("name")

2.3 RDD、DataSet、DataFrame之间的转换总结

1、 RDD -> DataFrame : 
rdd.map(para=>(para(0).trim(),para(1).trim().toInt)).toDF("name","age") //通过反射来设置
rdd.map(attributes => Person(attributes(0), attributes(1).trim.toInt)).toDF()//通过编程方式来设置Schema,适合于编译期不能确定列的情况
schemaString.map(fieldName => StructField(fieldName, StringType, nullable = true))  
val schema = StructType(fields)
val rdd[Row] = rdd.map(attributes => Row(attributes(0), attributes(1).trim))
val peopeDF = spark.createDataFrame(rdd[Row],schema)
2、 DataFrame -> RDD :   dataFrame.rdd      注意输出:Array([Michael,29], [Andy,30], [Justin,19])

1、 RDD -> DataSet   :   rdd.map(para=> Person(para(0).trim(),para(1).trim().toInt)).toDS
2、 DataSet ->  RDD  :  dataSet.rdd        注意输出: Array(Person(Michael,29), Person(Andy,30), Person(Justin,19))

1、 DataFrame -> DataSet:  dataFrame.to[Person]  
2、 DataSet -> DataFrame: dataSet.toDF      

2.4 对于DataFrame Row对象的访问方式

1、DataFrame = DataSet[Row], DataFrame里面每一行都是Row对象
2、如果需要访问Row对象中的每一个元素,你可以通过下标 row(0);你也可以通过列名 row.getAs[String]("name")

参考文章:

  1. SQL在Spark的解析过程(一)
    https://blog.csdn.net/ct2020129/article/details/94838954
  2. SQL在Spark的解析过程(二)
    https://blog.csdn.net/ct2020129/article/details/94839132
  3. SQL在Spark的解析过程(三)
    https://blog.csdn.net/ct2020129/article/details/94839805

猜你喜欢

转载自blog.csdn.net/weixin_42526352/article/details/104831164