在我们的Spark流媒体应用程序中,有60个批次,我们在DF上创建一个临时表,然后对它运行大约80个查询,如:
sparkSession.sql("select ... from temp_view group by ...")
但考虑到这些是大约300个求和列的相当繁重的查询,如果我们不必分析sql并生成每个微量分析的查询计划,那将是很好的。
是否有办法生成,缓存和重用查询计划?即使每个查询只保存50毫秒,每批也可节省大约4秒。
我们在CDH / YARN上使用Spark 2.2。感谢。
答案 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中已经发生了变化。