将复杂的地图操作表示为代码中的一系列链式地图任务而不是一个大型操作通常更为清晰。我知道Spark DAG Scheduler会执行优化,但它是否也会以这种方式优化链接操作?
这是一个人为的例子,其中从CSV字段中提取了不同日期的列表。:
csv.map(row => row.split(","))
.map(row => row(6)) // extract the proper field
.map(date_field => DateTime.parse(date_field).withTimeAtStartOfDay())
.distinct()
此示例是否会更有效,因为一个地图操作后跟distinct()
?
答案 0 :(得分:2)
猜猜我会把我的评论变成答案,因为没有其他人决定回答。基本上这是具有惰性DAG架构的要点之一。因为在看到最终的DAG之前不会执行任何操作,所以优化组合操作不需要随机播放是相对微不足道的(我会看看是否可以找到实际的代码)。假设你有一堆连续的地图,火花知道它可以丢弃前一个地图的结果,除非你缓存,缓存可以防止RDD不必重新计算,如果你多次使用它。因此,合并到1个地图功能将不会超过微优化,并且当您考虑许多MR样式作业是IO绑定时可能没有效果。
更新:通过查看spark用户列表,似乎Stage
可以有多个任务,特别是可以像地图一样链接在一起的任务可以放在一个阶段。
答案 1 :(得分:1)
简答:是的,但仅限于线性依赖。
长答案:比较Spark SQL / DataFrame的查询优化器,几乎不存在。
Spark核心API不会重写DAG的执行计划,即使它明显有益。这是一个例子:
考虑DAG:
A > B > D
> C >
收集D并且A不存在(持续存在是一项昂贵的操作,如果您不知道是否会收集D,则无法决定何时取消它)。理想情况下,优化器应将此DAG转换为线性&更便宜的A>元组2(B,C)> D.所以让我们测试一下:
val acc = sc.accumulator(0)
val O = sc.parallelize(1 to 100)
val A = O.map{
v =>
acc += 1
v * 2
}
val B = A.map(_*2)
val C = A.map(_*3)
val D = B.zip(C).map(v => v._1 + v._2)
D.collect()
assert(acc.value == 100)
结果?
200 did not equal 100
很明显,未经优化的DAG已被执行。
此外,从未提出过这样的特征(或任何接近的特征,例如广播加入/混洗加入基于成本的优化器)。可能是因为大多数Spark开发人员更喜欢直接控制执行,或者与SQL查询优化器相比,这种优化的效果非常有限。