为什么执行第一次和第二次Spa​​rk sql查询的执行时间不同?

时间:2019-04-06 11:16:29

标签: apache-spark apache-spark-sql

我正在使用spark sql在镶木地板数据源上运行一些聚合查询。

我的实木复合地板数据源包括一个具有以下列的表:id int,time timestamp,location int,counter_1 long,counter_2 long,...,counter_48。总数据大小约为887 MB。

我的spark版本是2.4.0。我在一台计算机(4核,16G内存)上运行一个主机和一个从机。

使用spark-shell,我运行了spark命令:

spark.time(spark.sql("SELECT location, sum(counter_1)+sum(counter_5)+sum(counter_10)+sum(counter_15)+sum(cou
nter_20)+sum(counter_25)+sum(counter_30)+sum(counter_35 )+sum(counter_40)+sum(counter_45) from parquet.`/home/hungp
han227/spark_data/counters` group by location").show())

执行时间为17秒。

第二次我运行类似的命令(仅更改列):

spark.time(spark.sql("SELECT location, sum(counter_2)+sum(counter_6)+sum(counter_11)+sum(counter_16)+sum(cou
nter_21)+sum(counter_26)+sum(counter_31)+sum(counter_36 )+sum(counter_41)+sum(counter_46) from parquet.`/home/hungp
han227/spark_data/counters` group by location").show())

执行时间约为3s。

我的第一个问题是:为什么它们不同?我知道由于拼花格式,这不是数据缓存。是重用查询计划之类的东西吗?

我做了另一个测试:第一个命令是

spark.time(spark.sql("SELECT location, sum(counter_1)+sum(counter_5)+sum(counter_10)+sum(counter_15)+sum(cou
nter_20)+sum(counter_25)+sum(counter_30)+sum(counter_35 )+sum(counter_40)+sum(counter_45) from parquet.`/home/hungp
han227/spark_data/counters` group by location").show())

执行时间为17秒。

在第二个命令中,更改聚合函数:

spark.time(spark.sql("SELECT location, avg(counter_1)+avg(counter_5)+avg(counter_10)+avg(counter_15)+avg(cou
nter_20)+avg(counter_25)+avg(counter_30)+avg(counter_35 )+avg(counter_40)+avg(counter_45) from parquet.`/home/hungp
han227/spark_data/counters` group by location").show())

执行时间约为5s。

我的第二个问题是:为什么第二个命令比第一个命令快,但是执行时间差却比第一个场景小?

最后,我遇到了与上述情况有关的问题:大约有200个公式,例如:

formula1 = sum(counter_1)+sum(counter_5)+sum(counter_10)+sum(counter_15)+sum(cou
nter_20)+sum(counter_25)+sum(counter_30)+sum(counter_35 )+sum(counter_40)+sum(counter_45)

formula2 = avg(counter_2)+avg(counter_5)+avg(counter_11)+avg(counter_15)+avg(cou
nter_21)+avg(counter_25)+avg(counter_31)+avg(counter_35 )+avg(counter_41)+avg(counter_45)

我必须经常运行以下格式:

select formulaX,formulaY, ..., formulaZ from table where time > value1 and time < value2 and location in (value1, value 2...) group by location

我的第三个问题是:是否有必要优化性能(一次使用的查询如果将来再次使用应该更快)?火花会优化自身还是我必须编写一些代码,更改配置?

2 个答案:

答案 0 :(得分:2)

这称为Exchange重用。当Spark运行改组(即聚合,联接)时,它会将改组数据的副本存储在本地工作节点上,以供潜在重用。这是一种内部控制的行为,不能受到最终用户的直接影响。如果发现自己一直在重复使用数据的特定部分(或查询结果),则可以考虑使用cache()显式地缓存它。但是,请记住,尽管这使Spark可以重用缓存的结果以提高潜在的查询性能(并且仅当您的缓存查询的分析器计划与您的新查询匹配时),但是过度使用CACHE可能会导致很多其他性能问题。 / p>

一个不好的例子是,当您的数据集很大时,可能会导致磁盘溢出问题。也就是说,数据集无法容纳到群集的可用内存中,需要将其写入速度较慢的硬盘。

另一个不好的例子是当您的查询仅需要访问缓存数据的子集时。通过在内存中缓存整个数据集,Spark被迫执行完整的内存表扫描。与完全不使用缓存相反,这不仅浪费资源,而且导致查询性能降低。

最好的办法是尝试使用一些您自己的示例查询尝试并出错,查看Spark UI并检查是否存在磁盘溢出迹象或大量输入数据扫描。

每种查询/数据组合都是唯一的,因此您需要进行一些试验,以找到适合自己工作负载的最佳性能调整方法。

答案 1 :(得分:0)

进行聚合火花时会创建所谓的随机文件。如果运行两次相同的查询,它将重复使用随机存储在工作程序fs上的随机文件。不幸的是,您不能依靠它们始终存在,因为最终文件处理程序会被gc'd处理。如果您要在同一个数据集上运行10个查询,请对其进行缓存或使用数据块。