apache-spark - 火花 sql 执行 except() 查询需要很多时间
问题描述
我在火花应用程序中有以下代码
val log_file_df = loadInputFile() /*loads log file as Dataframe */
val split_df: DataFrame = splitLineByDelimiter(log_file_df) /* applies filter
function to input file */
val bad_data_df: DataFrame = parseAndSaveBadData(split_df) /*filters split dataframe into bad data */
val good_data_df = split_df.except(bad_data_df) /* separates good data from bad data */
如果我在 split_df,bad_data_df 上运行诸如 show() 之类的任何操作,它的执行时间会更短(大约 1.5 分钟),并且我检查了物理计划输入日志文件是否只读一次
但是如果我对好的数据执行任何操作,它会花费更多的时间。(4 分钟)
val good_data_df = split_df.except(bad_data_df).show()
从物理计划输入日志文件读取两次。我尝试了以下选项
split_df.cache() or split_df.createOrReplaceTempView("split_dfTable")
// Init.getSparkSession.sqlContext.cacheTable("split_dfTable")
val splitbc = Init.getSparkSession.sparkContext.broadcast(split_df)
但是执行时间没有改善,物理计划是一样的。这是物理计划。我应该如何改进我的代码?我的 good_data_df 被进一步转换并与其他一些需要更多时间的数据框连接。
good_data_df.show(false)good_data_df.explain(true)
+- Exchange hashpartitioning(hostname#16, date#17, path#18, status#19,
content_size#20, 200)
+- *HashAggregate(keys=[hostname#16, date#17, path#18, status#19,
content_size#20], functions=[], output=[hostname#16, date#17, path#18,
status#19, content_size#20])
+- SortMergeJoin [coalesce(hostname#16, ), coalesce(date#17, ),
coalesce(path#18, ), coalesce(status#19, ),
coalesce(content_size#20, )],
[coalesce(hostname#49, ), coalesce(date#50, ), coalesce(path#51, ),
coalesce(status#52, ), coalesce(content_size#53, )], LeftAnti,
(((((hostname#16 <=> hostname#49) && (date#17 <=> date#50)) && (path#18 <=>
path#51)) && (status#19 <=> status#52)) && (content_size#20 <=>
content_size#53))
:- *Sort [coalesce(hostname#16, ) ASC NULLS FIRST, coalesce(date#17, ) ASC
NULLS FIRST, coalesce(path#18, ) ASC NULLS FIRST, coalesce(status#19, ) ASC
NULLS FIRST, coalesce(content_size#20, ) ASC NULLS FIRST], false, 0
: +- Exchange hashpartitioning(coalesce(hostname#16, ), coalesce(date#17, ),
coalesce(path#18, ), coalesce(status#19, ), coalesce(content_size#20, ), 200)
: +- *Project [regexp_extract(val#13, ^([^\s]+\s), 1) AS hostname#16,
regexp_extract(val#13, ^.*(\d\d/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} -\d{4}), 1) AS
date#17, regexp_extract(val#13, ^.*"\w+\s+([^\s]+)\s*[(HTTP)]*.*", 1) AS
path#18, regexp_extract(val#13, ^.*"\s+([^\s]+), 1) AS status#19,
regexp_extract(val#13, ^.*\s+(\d+)$, 1) AS content_size#20]
: +- *FileScan csv [val#13] Batched: false, Format: CSV, Location:
InMemoryFileIndex[file:/C:/Users/M1047320/Desktop/access_log_Jul95],
PartitionFilters: [], PushedFilters: [], ReadSchema: struct<val:string>
+- *Sort [coalesce(hostname#49, ) ASC NULLS FIRST, coalesce(date#50, ) ASC
NULLS FIRST, coalesce(path#51, ) ASC NULLS FIRST, coalesce(status#52, ) ASC
NULLS FIRST, coalesce(content_size#53, ) ASC NULLS FIRST], false, 0
+- Exchange hashpartitioning(coalesce(hostname#49, ),
coalesce(date#50, ), coalesce(path#51, ), coalesce(status#52, ),
coalesce(content_size#53, ), 200)
+- *Project [regexp_extract(val#13, ^([^\s]+\s), 1) AS hostname#49,
regexp_extract(val#13, ^.*(\d\d/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} -\d{4}), 1) AS
date#50, regexp_extract(val#13, ^.*"\w+\s+([^\s]+)\s*[(HTTP)]*.*", 1) AS
path#51, regexp_extract(val#13, ^.*"\s+([^\s]+), 1) AS status#52,
regexp_extract(val#13, ^.*\s+(\d+)$, 1) AS content_size#53]
+- *Filter ((((regexp_extract(val#13, ^.*"\w+\s+([^\s]+)\s*
[(HTTP)]*.*", 1) RLIKE .*(jpg|gif|png|xbm|jpeg|wav|mpg|pl)$ ||
(regexp_extract(val#13, ^([^\s]+\s), 1) = )) || (regexp_extract(val#13,
^.*"\w+\s+([^\s]+)\s*[(HTTP)]*.*", 1) = )) || (regexp_extract(val#13, ^.*
(\d\d/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} -\d{4}), 1) = )) ||
(regexp_extract(val#13, ^.*"\s+([^\s]+), 1) = ))
+- *FileScan csv [val#13] Batched: false, Format: CSV,
Location:
InMemoryFileIndex[file:/C:/Users/M1047320/Desktop/access_log_Jul95],
PartitionFilters: [], PushedFilters: [], ReadSchema: struct<val:string>
解决方案
显示 split_df,bad_data_df 花费的时间要少得多的原因是Spark 只会读取和解析 show 所需的行。您读入 Spark 的数据被分解为Partitions
,这些数据片段将在工作人员之间划分。
一旦你在 split_df 上调用 show,bad_data_df Spark 将只对一小部分数据起作用(split_df 上只有 20 行,bad_data_df 上只有前 20 行坏行)。
另一方面,当在 good_data_df 上调用 show 时,Spark 将不得不处理所有数据(以读取所有数据、解析数据以及从总数中排除坏行为代价)。
如果您有一种定义坏行的简单方法,我建议添加另一列带有UDF - Boolean isBad,并对其进行过滤。简单地传递数据比except
.
推荐阅读
- excel - 修复将空单元格转换为日期的 VBA 代码
- sqlite - 当只出现一个查询结果时,如何保存多个查询结果?
- c# - 无法获得 z.rotation
- javascript - 找到一个只有不同对象的数组
- c++ - 我们可以将数据附加到 FlatBuffer 中的 Vector 吗?
- angular - 如何在自定义验证器中访问组件变量?
- loops - 带循环的ansible注册
- reactjs - 使用 Material-UI-Picker 在 iOS 设备上错误地显示时间
- arrays - 如何使用firestore根据值检索数组内的数组
- angular - 将材料数据表排序为仅按角度升序排列