如何在每次执行查询时避免查询准备(解析,计划和优化)?

时间:2018-03-31 01:43:34

标签: apache-spark apache-spark-sql spark-streaming

在我们的Spark流媒体应用程序中,有60个批次,我们在DF上创建一个临时表,然后对它运行大约80个查询,如:

sparkSession.sql("select ... from temp_view group by ...")

但考虑到这些是大约300个求和列的相当繁重的查询,如果我们不必分析sql并生成每个微量分析的查询计划,那将是很好的。

是否有办法生成,缓存和重用查询计划?即使每个查询只保存50毫秒,每批也可节省大约4秒。

我们在CDH / YARN上使用Spark 2.2。感谢。

1 个答案:

答案 0 :(得分:1)

我之前没有尝试过,但“生成,缓存并重用查询计划”你应该简单地(重新)使用查询(它可能不一定是“形状”你经常使用,但有一个可能适用于你的情况)。

(大声思考)

每个结构化查询(无论是数据集,数据框架还是SQL)都要经历各个阶段,即解析,分析,逻辑优化,规划和物理优化。

结构化查询由其​​计划描述,优化的物理查询计划是您可以使用Dataset.explain查看的计划:

  

explain():Unit 将物理计划打印到控制台以进行调试。

scala> spark.version
res0: String = 2.3.1-SNAPSHOT

scala> :type q
org.apache.spark.sql.DataFrame

scala> q.explain
== Physical Plan ==
*(1) Project [id#0L, (id#0L * 2) AS x2#2L]
+- *(1) Range (0, 4, step=1, splits=8)

您不直接使用该计划,但重点是您可以。另一个重要的一点是,计划通常对他们优化的数据集一无所知(我说通常是因为Spark SQL有一个基于成本的优化器,可以使用可以提供最优化的查询计划的数据。)

每当执行操作时,查询都会通过所谓的结构化查询执行管道。并且每次执行一个动作时它都会进行“预处理”(即使这是同一个动作)。这就是为什么你可以缓存结果,但这会将查询与数据永远联系在一起(你想避免)。

话虽如此,我认为你可以在调用一个动作之前进行优化(并通过查询的“管道”抽取数据)。只需使用您可以使用QueryExecution.rdd生成的优化物理查询计划,该计划将为您提供代表结构化查询的RDD。使用该RDD,您可以简单地RDD.[theAction]每个批处理间隔,以避免结构化查询通过的所有阶段成为RDD。

scala> q.rdd
res2: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[4] at rdd at <console>:26

您甚至可以使用QueryExecution.toRdd来“优化”RDD。

scala> q.queryExecution.toRdd
res4: org.apache.spark.rdd.RDD[org.apache.spark.sql.catalyst.InternalRow] = MapPartitionsRDD[7] at toRdd at <console>:26

但是(再次,大声思考)所有这些重用都是自动发生的,因为阶段是lazy val所以只是......不能它不能工作......忽视最后一个“但是”并坚持想法重用底层RDD :)它应该可以工作。

BTW,这几乎就是Spark Structured Streaming用于微批处理的每批(间隔)。但是在2.3中已经发生了变化。