我在本地运行HDFS和Spark,并尝试了解Spark持久性的工作原理。我的目标是将连接的数据集存储在内存中,然后即时运行查询。但是,我的查询似乎是重做连接而不是简单地扫描持久的预连接数据集。
我创建并保留了两个数据帧,让我们说df1和df2,加载来自HDFS的两个CSV文件。我坚持将两个数据帧连接在内存中:
val result = df1.join(df2, "USERNAME")
result.persist()
result.count()
然后我在结果之上定义了一些操作:
val result2 = result.select("FOO", "BAR").groupBy("FOO").sum("BAR")
result2.show()
' RESULT2'不会依赖于持久化结果并自行重做连接。以下是结果和结果2的物理计划:
== Physical Plan for result ==
InMemoryColumnarTableScan [...], (InMemoryRelation [...], true, 10000, StorageLevel(true, true, false, true, 1), (TungstenProject [...]), None)
== Physical Plan for result2 ==
TungstenAggregate(key=[FOO#2], functions=[(sum(cast(BAR#10 as double)),mode=Final,isDistinct=false)], output=[FOO#2,sum(BAR)#837])
TungstenExchange hashpartitioning(FOO#2)
TungstenAggregate(key=[FOO#2], functions=[(sum(cast(BAR#10 as double)),mode=Partial,isDistinct=false)], output=[FOO#2,currentSum#1311])
InMemoryColumnarTableScan [FOO#2,BAR#10], (InMemoryRelation [...], true, 10000, StorageLevel(true, true, false, true, 1), (TungstenProject [...]), None)
我天真地认为,由于连接已经完成并在内存中进行了分区,因此第二个操作将简单地包含每个分区上的聚合操作。从头开始重做连接应该更昂贵。我是假设错误还是做错了什么?另外,这是保留连接数据集以供以后查询的正确模式吗?
编辑:对于记录,在我调低了shuffle分区的数量之后,第二个查询变得更加高效。默认情况下,spark.sql.shuffle.partitions设置为200.在我的本地实例上将其设置为1可以显着提高性能。
答案 0 :(得分:3)
如果我们查看该计划,我们会看到Spark实际上正在使用缓存数据而不是重做连接。从下往上:
这是Spark从缓存中读取数据:
InMemoryColumnarTableScan [FOO#2,BAR#10], (InMemoryRelation ...
这是Spark在每个分区中通过FOO聚合BAR - 查找mode = Partial
TungstenAggregate(key=[FOO#2], functions=[(sum(cast(BAR#10 as double)),mode=Partial ...
这是Spark改组上一步的每个分区的数据:
TungstenExchange hashpartitioning(FOO#2)
这是Spark聚合洗牌分区总和 - 查找mode = Final
TungstenAggregate(key=[FOO#2], functions=[(sum(cast(BAR#10 as double)),mode=Final ...
阅读这些计划有点痛苦,所以如果您可以访问Spark UI的SQL选项卡(我认为1.5+),我建议您使用它。