目前还是在封闭中,只能继续在家学习工作。今天学习 Spark SQL 执行全过程概述,包括内部的一些基本概念和数据结构。

1.SQL 到 RDD

使用 Spark SQL 进行数据分析的一般步骤如下:

// 第一步 初始化 SparkSession
val spark = SparkSession.builder()
						.appName("example")
						.master("local")
						.getOrCreate()

// 第二步 创建数据表并读取
spark.read.json("student.json")
	.createOrReplaceTempView("student")

// 第三步 通过SQL查询
spark.sql("select name from student where age > 18").show()

这里的 student.json 如下:

{"id":1,"name":"Kate","age":29}
{"id":2,"name":"Andy","age":30}
{"id":3,"name":"Tony","age":10}

对于 Spark SQL,从 SQL 到 RDD 执行过程如下:

SQL 查询 → Spark SQL 逻辑计划 → Spark SQL 物理计划 → show()。

其中的逻辑计划又细分为三步:

  • 逻辑算子树:数据结构,不含数据信息;
  • 解析后的逻辑算字树:节点中绑定各种信息;
  • 优化后的逻辑算子树:对低效的逻辑计划进行转换。

物理计划也细分为三步:

  • 根据逻辑算子树生成物理算字树列表(一对多);
  • 选择最优的物理算子树;
  • 提交前准备。

上述过程都在 Spark 集群的Driver 端进行。实际过程如下图:

spark-SQl的命令行 spark执行sql_spark

2.概念

Spark SQL 实现上述流程的基础框架是 Catalyst,下面介绍一下其基本概念。

2.1 InternalRow

Spark SQL 内部实现中 InternalRow 用来表示一行数据。InternalRow 作为抽象类包含 numFields 和 update 方法以及 get 和 set 方法。

spark-SQl的命令行 spark执行sql_big data_02

  • BaseGenericInternalRow:实现了所有 get 类型的方法。
  • JoinedRow:Join 操作,两个 InternalRow 放在一起形成新的 InternalRow。
  • UnsafeRow:不采用 Java 对象存储的方式,避免了 JVM 中垃圾回收(GC)的代价。并且采用特点编码,存储更加高效。
2.2 TreeNode

TreeNode 是 Spark SQL 所有树结构的基类。

spark-SQl的命令行 spark执行sql_spark_03

提供了树的遍历等相关的方法:

  • collectLeaves:获取当前 TreeNode 所有叶子节点;
  • collectFirst:先序遍历返回第一个满足条件的节点;
  • withNewChildren:当前节点替换为新节点;
  • transformDown:通过先序遍历将规则用于所有节点;
  • transformUp:通过后序遍历将规则用于所有节点;
  • transformChildren:递归地将规则作用于所有节点。
2.3 Expression

表达式(expression)一般是不需要触发执行引擎而能够直接进行计算的单元,例如加减乘除、逻辑运算、转换操作、过滤操作。看上面的图我们知道,Expression 也是 TreeNode 类的子类,因此可以使用 TreeNode 的方法。

下图展示了 Expression 的基本操作。

spark-SQl的命令行 spark执行sql_SQL_04

3.内部数据类型系统

常见的数据类型包括整数、浮点数、字符串,以及复杂的嵌套结构。用一张图展示数据类型系统以及它们之间的关系。

spark-SQl的命令行 spark执行sql_spark-SQl的命令行_05

4.总结

今天是一个简单的入门。要记住的是 SQL 解析生成 RDD 过程中包括逻辑计划和物理计划两个重要的阶段,从宏观上有个理解。后面再一步步学习 Aggregation、join 等具体实现。