大数据开发面试题之Spark 3.0特性
1、动态分区裁剪(Dynamic Partition Pruning)
在3.0以前,spark是不支持动态分区的,所谓动态分区就是针对分区表中多个表进行join的时候基于运行时(runtime)推断出来的信息,在on后面的条件语句满足一定的要求后就会进行自动动态分区裁减优化。
SELECT t1.id,t2.pKey FROM t1 JOIN t2 ON t1.pKey = t2.pKey AND t2.id < 2;上面这条SQL语句在没有使用动态分区的情况下执行过程如下图所示:
由于之前版本的 Spark 无法进行动态计算代价,所以可能会导致t1表扫描出大量无效的数据。
在使用了动态分区以后执行过程变成了下图:
2、自适应查询执行(Adaptive Query Execution)
自适应查询是指对执行计划按照实际数据分布和组织情况,评估其执行所消耗的时间和资源,从而选择代价最小的计划去执行。一般数据库的优化器有两种,一种是基于规则的优化器(RBO),一种是基于代价的优化器(CBO),自适应查询指的就是对CBO的优化,Spark SQL的运行流程主要有:
1)将SQL语句通过词法和语法解析生成未绑定的逻辑执行计划;
2)分析器配合元数据使用分析规则,完善未绑定的逻辑计划属性转换成绑定的逻辑计划;
3)优化器使用优化规则将绑定的逻辑计划进行合并、裁减、下推生成优化的逻辑计划;
4)规划器使用规划策略把优化的逻辑计划转换成可执行的物理计划;
5)进行preparations规则处理,构建RDD的DAG图执行查询计划。
Spark以前的调度规则是执行计划一旦确定,即使发现后续执行计划可以优化,也不可更改,而自适应查询功能则是在执行查询计划的同时,基于表和列的统计信息,对各个算子产生的中间结果集大小进行估算,根据估算结果来动态地选择最优执行计划,如下图:
上图是一个经典的Spark的流程,从Parser、Analyzer、Optimizer、Planner到Query的执行。该版本中,AQE指的是图中的红线部分。当某种condition满足的情况下可以进行动态自适应规划。
下面举一个简单的例子,执行的是两个表之间的join查询。包含一个key和Filter,如t2.col2 LIKE '9999%'。
在基于cost的模型中是不可能准确的知道Filter能排除多少行的,这种情况下Spark通过谓词下推,将各个条件先应用到对应的数据上,而不是根据写入的顺序执行,就可以先过滤掉部分数据,降低join等一系列操作的数据量级。
在没有动态实时运行信息的时候,保守估计判断只能用SortMergeJoin。
当收集到运行时信息后会发现某个Filter事实上已经去掉了表中绝大多数行,完全可以采用BroadcastHashJoin,如果上层parent也有这种情况,就可以大大提升查询效率。
3、***感知调度(Accelerator-aware Scheduling)
如今大数据和机器学习已经有了很大的结合,在机器学习里面,因为计算迭代的时间可能会很长,开发人员一般会选择使用 GPU、FPGA 或 TPU 来加速计算。
在 Apache Hadoop 3.1 版本里面已经开始内置原生支持 GPU 和 FPGA 了。作为通用计算引擎的 Spark 肯定也不甘落后,来自 Databricks、NVIDIA、Google 以及阿里巴巴的工程师们正在为 Apache Spark 添加原生的 GPU 调度支持,该方案填补了 Spark 在 GPU 资源的任务调度方面的空白,有机地融合了大数据处理和 AI 应用,扩展了 Spark 在深度学习、信号处理和各大数据应用的应用场景。
为了让 Spark 也支持 GPUs,在技术层面上需要做出两个主要改变:
1)在 cluster manager 层面上,需要升级 cluster managers 来支持 GPU。并且给用户提供相关 API,使得用户可以控制 GPU 资源的使用和分配。
2)在 Spark 内部,需要在 scheduler 层面做出修改,使得 scheduler 可以在用户 task 请求中识别 GPU 的需求,然后根据 executor 上的 GPU 供给来完成分配。
因为让 Apache Spark 支持 GPU 是一个比较大的特性,所以项目分为了几个阶段。
1)在 Apache Spark 3.0 版本,将支持在 standalone、 YARN 以及 Kubernetes 资源管理器下支持 GPU,并且对现有正常的作业基本没影响。
2)对于 TPU 的支持、Mesos 资源管理器中 GPU 的支持、以及 Windows 平台的 GPU 支持将不是这个版本的目标。
3)而且对于一张 GPU 卡内的细粒度调度也不会在这个版本支持;Spark 3.0 版本将把一张 GPU 卡和其内存作为不可分割的单元。
4、更好的API扩展(BetterAPI-Extensions-DataSourceV2)
Data Source API 定义如何从存储系统进行读写的相关 API 接口,比如 Hadoop 的 InputFormat/OutputFormat,Hive 的 Serde 等。这些 API 非常适合用户在 Spark 中使用 RDD 编程的时候使用。使用这些 API 进行编程虽然能够解决我们的问题,但是对用户来说使用成本还是挺高的,而且 Spark 也不能对其进行优化。
为了解决这些问题,Spark 1.3 版本开始引入了 Data Source API V1,通过这个 API 我们可以很方便的读取各种来源的数据,而且 Spark 使用 SQL 组件的一些优化引擎对数据源的读取进行优化,比如列裁剪、过滤下推等等。
Data Source API V1 为我们抽象了一系列的接口,使用这些接口可以实现大部分的场景。但是随着使用的用户增多,逐渐显现出一些问题:
1)部分接口还是太过于依赖SQLContext和DataFrame
2)缺乏对列式数据库存储的读取支持
3)写操作不支持事务
4)不支持流式处理,不能进行增量跌打
5)缺乏分区和排序信息
6)扩展能力有限,难以下推其它算子
为了解决 Data Source V1 的一些问题,从 Apache Spark 2.3.0 版本开始,社区引入了 Data Source API V2,在保留原有的功能之外,还解决了 Data Source API V1 存在的一些问题,比如不再依赖上层 API,扩展能力增强。虽然这个功能在 Apache Spark 2.x 版本就出现了,但是不是很稳定,所以社区对 Spark DataSource API V2 的稳定性工作以及新功能分别开了两个 ISSUE:SPARK-25186 以及 SPARK-22386。
Spark DataSource API V2 最终稳定版以及新功能将会随着年底和 Apache Spark 3.0.0 版本一起发布,其也算是Spark 3.0版本的一大新功能。
5、更好的ANSI-SQL兼容(ANSI SQL Compatible)
因为Spark原来的SQL语法和函数跟ANSI标准还是存在一些差异,因此这次版本更新将缩小和ANSI标准之间的差异,包括增加一些ANSI SQL的函数、区分SQL保留关键字以及内置函数等。
6、SparkR 中的向量化(Vectorization in SparkR)
Spark 是从 1.4 版本开始支持 R 语言的,但是那时候 Spark 和 R 进行交互的架构图如下:
SparkR 向量化允许用户按原样使用现有代码,但是当他们执行 R 本地函数或将 Spark DataFrame 与 R DataFrame 互相转换时,可以将性能提高大约数千倍。这项工作可以看下 Spark-26759。新的架构如下: