小记--------sparksql执行全过程

 1 案例
 2 def main(args: Array[String]): Unit = {
 3  
 4     // 1.创建sparkconf
 5   val conf = new SparkConf()
 6     .setMaster("local")
 7     .setAppName("test-sql")
 8  
 9  
10   // 2.创建sparksession
11   val session: SparkSession = SparkSession
12     .builder()
13     .config(conf)
14     .getOrCreate()
15  
16  
17   // 3.创建数据表并读取数据 , 并创建了student的数据表(视图)
18     // 读取本地student.json 文件。
19   //{"id": 1 , "name" : "Kate" , "age" : 29}
20   //{"id": 2 , "name" : "Andy" , "age" : 39}
21   //{"id": 3 , "name" : "Tony" , "age" : 10}
22   session
23     .read
24     .json("D:\\daima\\work\\1011\\spark-test-zhonghuashishan\\src\\test\\file\\student.json")
25     .createOrReplaceTempView("student")
26  
27  
28   // SQL查询
29   session.sql("select name from student where age > 18 ").show()
30 }

 

 
一般来讲,对于sparkSQL系统,从SQL到spark中的RDD的执行需要经过两个大的阶段、
    逻辑计划(LogicalPlan)
    物理计划(PhysicalPlan)
SQL执行过程概览
 
逻辑计划阶段
    会将用户所写的SQL语句转换成树型数据结构(逻辑算子树),SQL语句中蕴含的逻辑映射到逻辑算子树的不同节点,
    逻辑计划阶段生成的逻辑算子树并不会直接提交执行,仅作为中间阶段。
 
    逻辑算子树的生成过程经历3个子阶段
        1.未解析的逻辑算子树;仅仅是数据结构,不包含任何数据信息等
        2.解析后的逻辑算子树;节点中绑定各种信息
        3.优化后的逻辑算子树;应用各种优化规则对一些低效的逻辑计划进行转换
 
物理计划阶段
    将上一步逻辑计划阶段生成的逻辑算子树进行进一步转换,生成物理算子树。
    物理算子树的节点会直接生成RDD或对RDD进行transformation操作(注:每个物理计划节点中都实现了对RDD进行转换的execute方法)
 
    物理计划阶段的3个子阶段
        1.物理算子树的列表;(注:同样的逻辑算子树可能对应多个物理算子树)
        2.最优物理算子树;从算子树列表中按照一定的策略选取最优的物理算子树,然后对选取的物理算子树进行提交前的准备工作;例如:确保分区操作正确,物理算子树节点重用,执行代码生成等
        3.准备后的物理算子树;对物理算子树生成的RDD执行action操作,即可提交程序
 
SQL语句的解析一直到提交之前,整个转换过程都在spark集群的Driver端进行不涉及分布式环境。
 
 
Catalyst
sparkSQL内部实现流程中平台无关部分的基础框架称为Catalyst,它主要包括InternalRow体系、TreeNode体系和Expression体系。
 
InternalRow体系
    spark SQL 内部实现中,InternalRow就是用来表示一行行数据的类,物理算子树节点产生和转换的RDD类型即为RDD[InternalRow] 。 InternalRow中的每一列都是Catalyst内部定义的数据类型。
 
InternalRow作为一个抽象类,包含numFields和update方法,以及各列数据对应的get与set方法,InternalRow中都是根据下表来访问和操作列元素的。
    其具体的实现包括BaseGenericInternalRow、UnsafeRow和JoinedRow3个直接子类
 
InternalRow体系
 
BaseGenericInternalRow:同样是一个抽象类,实现了InternalRow中定义的所有get类型方法,这些方法的实现都通过调用类中定义的genericGet虚函数进行,该函数的实现在下一级子类中(也就是GenericInternalRow 、 SpecificInternalRow 、 MutbaleUnsafeInternalRow类中)
 
JoinedRow:该类主要用于Join操作,将两个InternalRow放在一起形成新的InternalRow。使用时需要注意构造参数的顺序。
 
UnsafeRow:不采用java对象存储的方式,避免了JVM中垃圾回收(GC)的代价。此外UnsafeRow对行数据进行了特定的编码,使得存储更加高效。
 
BaseGenericInternalRow也有3个子类,分别是GenericInternalRow、SpecificInternalRow和 MutableUnsafeRow类。
    其中MutableUnsafeRow和UnsafeRow相关,用来支持对特定的列数据进行修改。
 
GenericInternalRow类源码
//构造参数是Array[Any]类型,采用对象数组进行底层存储、
// 注意:数组是非拷贝的,因此一但创建,就不允许通过set操作进行改变。
 1 class GenericInternalRow(val values: Array[Any]) extends BaseGenericInternalRow {
 2   /** No-arg constructor for serialization. */
 3   protected def this() = this(null)
 4  
 5  
 6   def this(size: Int) = this(new Array[Any](size))
 7  
 8 // 也是直接根据下表访问的
 9   override protected def genericGet(ordinal: Int) = values(ordinal)
10  
11  
12   override def toSeq(fieldTypes: Seq[DataType]): Seq[Any] = values.clone()
13  
14  
15   override def numFields: Int = values.length
16  
17  
18   override def setNullAt(i: Int): Unit = { values(i) = null}
19  
20  
21   override def update(i: Int, value: Any): Unit = { values(i) = value }
22 }
View Code

 

 
而SpecificInternalRow则是以Array[MutableValue]为构造参数的,允许通过set操作进行修改。
final class SpecificInternalRow(val values: Array[MutableValue]) extends BaseGenericInternalRow {
 
 
TreeNode体系
    无论是逻辑计划还是物理计划,都离不开中间数据结构,在Catalyst中,对应的是TreeNode体系,TreeNode类是Sparksql中所有树结构的基类,TreeNode内部包含一个Seq[BaseType]类型的变量children来表示节点,TreeNode定义了foreach、map、collect等针对节点操作方法,以及transformUp和transformDown等遍历节点并对匹配节点进行相应转换。
    TreeNode一直在内存里维护,不会dump到磁盘以文件形式存储,且无论在映射逻辑执行计划阶段还是优化逻辑执行计划阶段,树的修改都是以替换已有节点的方式进行。
 
TreeNode体系
 
TreeNode基本操作
除上述操作外,Catalyst中还提供了节点位置功能,即能够根据TreeNode定位到对应的SQL字符串中的行数和起始位置,该功能在SQL解析发生异常时能够方便用户迅速找到出错的地方
 1 // 在TreeNode类中
 2  
 3 case class Origin(
 4   line: Option[Int] = None,    // 行号
 5   startPosition: Option[Int] = None)   // 偏移量
 6  
 7 object CurrentOrigin {
 8   private val value = new ThreadLocal[Origin]() {
 9     override def initialValue: Origin = Origin()
10   }
11  
12  
13   def get: Origin = value.get()
14   def set(o: Origin): Unit = value.set(o)
15  
16  
17   def reset(): Unit = value.set(Origin())
18  
19  
20   def setPosition(line: Int, start: Int): Unit = {
21     value.set(
22       value.get.copy(line = Some(line), startPosition = Some(start)))
23   }
24  
25  
26   def withOrigin[A](o: Origin)(f: => A): A = {
27     set(o)
28     val ret = try f finally { reset() }
29     ret
30   }
31 }
View Code

 

posted @ 2020-04-19 00:22  二黑诶  阅读(1254)  评论(0编辑  收藏  举报