SparkSQL 体系结构

SparkSQL体系结构如下图所示,整体由上到下分为三层:编程模型层、执行任务优化层以及任务执行引擎层。

  1. SparkSQL编程模型可以分为SQL和DataFrame两种。
  2. 执行计划优化又称为Catalyst,该模块负责将SQL语句解析成AST(逻辑执行计划),并对原始逻辑执行计划进行优化,优化规则分为基于规则的优化策略和基于代价的优化策略两种,最终输出优化后的物理执行计划。
  3. 任务执行引擎就是Spark内核,负责根据物理执行计划生成DAG,在任务调度系统的管理下分解为任务集并分发到集群节点上加载数据运行,Tungsten基于对内存和CPU的性能优化,使得Spark能够更好地利用当前硬件条件提升性能。

SparkSQL 编程模型 - DataFrame

说到计算模型,批处理计算从最初提出一直到现在,一共经历了两次大的变革,第一次变革是从MR编程模式到RDD编程模型,第二次则是从RDD编程模式进化到DataFrame模式。

第一次变革:MR编程模型 -> DAG编程模型

和MR计算模型相比,DAG计算模型有很多改进:
1. 可以支持更多的算子,比如filter算子、sum算子等,不再像MR只支持map和reduce两种
2. 更加灵活的存储机制,RDD可以支持本地硬盘存储、缓存存储以及混合存储三种模式,用户可以进行选择。
3. DAG模型带来了更细粒度的任务并发,不再像MR那样每次起个任务就要起个JVM进程。另外,DAG模型带来了另一个利好是很好的容错性,一个任务即使中间断掉了,也不需要从头再来一次。
4. 延迟计算机制一方面可以使得同一个stage内的操作可以合并到一起落在一块数据上,而不再是所有数据先执行a操作、再扫描一遍执行b操作

第二次变革:DAG编程模型 -> DataFrame编程模型

相比RDD,DataFrame增加了scheme概念,从这个角度看,DataFrame有点类似于关系型数据库中表的概念。可以根据下图对比RDD与DataFrame数据结构的差别:

sparksql 执行建表语句 spark sql执行计划_优化

直观上看,DataFrame相比RDD多了一个表头,这个小小的变化带来了很多优化的空间:
RDD中每一行纪录都是一个整体,因此你不知道内部数据组织形式,这就使得你对数据项的操作能力很弱。表现出来就是支持很少的而且是比较粗粒度的算子,比如map、filter算子等。而DataFrame将一行切分了多个列,每个列都有一定的数据格式,这与数据库表模式就很相似了,数据粒度相比更细,因此就能支持更多更细粒度的算子,比如select算子、groupby算子、where算子等。

DataFrame的Schema的存在,数据项的转换也都将是类型安全的,这对于较为复杂的数据计算程序的调试是十分有利的,很多数据类型不匹配的问题都可以在编译阶段就被检查出来,而对于不合法的数据文件,DataFrame也具备一定分辨能力。

DataFrame schema的存在,开辟了另一种数据存储形式:列式数据存储。列式存储是相对于传统的行式存储而言的,简单来讲,就是将同一列的所有数据物理上存储在一起。对于列式存储和行式存储可以参考下图:

sparksql 执行建表语句 spark sql执行计划_优化_02

列式存储有两个重要的作用,首先,同一种类型的数据存储在一起可以很好的提升数据压缩效率,因为越“相似”的数据,越容易压缩。数据压缩可以减少存储空间需求,还可以减少数据传输过程中的带宽需求,这对于类似于Spark之类的大内存计算引擎来讲,会带来极大的益处;另外,列式存储还可以有效减少查询过程中的实际IO,大数据领域很多OLAP查询业务通常只会检索部分列值,而不是粗暴的select * ,这样列式存储可以有效执行’列值裁剪’,将不需要查找的列直接跳过。

DAG编程模式都是用户自己写RDD scala程序,自己写嘛,必然或多或少会有性能提升的空间!而DataFrame编程模式集成了一个优化神奇-Catalyst,这玩意类似于MySQL的SQL优化器,负责将用户写的DataFrame程序进行优化得到最优的执行计划。