我有以下结构化查询:
val A = 'load somedata from HDFS'.persist(StorageLevel.MEMORY_AND_DISK_SER)
val B = A.filter('condition 1')
val C = A.filter('condition 2')
val D = A.filter('condition 3')
val E = A.filter('condition 4')
val F = A.filter('condition 5')
val G = A.filter('condition 6')
val H = A.filter('condition 7')
val I = B.union(C).union(D).union(E).union(F).union(G).union(H)
我坚持使用数据帧A,这样当我使用B / C / D / E / F / G / H时,A数据帧应该只计算一次?但这项工作的DAG如下:
从上面的DAG看,第6-12阶段似乎全部执行了,数据帧A计算了7次?
为什么会发生这种情况?
也许DAG只是假的?我发现第7-12阶段的顶部没有线条,其中第6阶段确实有来自其他阶段的两条线
我没有列出所有操作。在union
操作后,我将I
数据帧保存到HDFS。 I
数据框上的此操作是否会使持久操作真正完成?或者我必须在count
数据帧上执行A
之类的操作操作,以便在重用A
数据帧之前触发持久操作吗?
答案 0 :(得分:3)
执行以下行不会保留数据集。
val A = 'load somedata from HDFS'.persist(StorageLevel.MEMORY_AND_DISK_SER)
与数据集API一起使用时,缓存/持久性是惰性的,因此您必须使用count
运算符或类似操作符触发缓存,然后再提交Spark作业。
之后,以下所有运营商filter
都应使用 InMemoryTableScan ,并在计划中使用绿点(如下所示)。
在您的情况下,即使union
之后数据集I
未被缓存,因为您尚未触发缓存(但仅将其标记为缓存)。
联合操作后,我将
I
数据帧保存到HDFS。I
数据框上的此操作是否会使持久操作真正完成?
是。只有操作(如保存到外部存储)才能触发持久性以供将来重用。
或者我必须执行一项操作操作,例如对
A
数据帧进行计数,以便在重用A
数据帧之前触发持久操作吗?
这就是重点!在您的情况下,由于您希望在A
运算符之间重用filter
数据帧,因此您应首先persist
,count
(以触发缓存),然后filter
第
在您的情况下,由filter
引起的任何性能提升都不会使persist
受益。 persist
实际上没有任何对性能的影响,只是让代码审核者认为不是这样。
如果您想查看数据集何时以及是否已缓存,可以在网页用户界面中查看存储标签,或者询问CacheManager
。
val nums = spark.range(5).cache
nums.count
scala> spark.sharedState.cacheManager.lookupCachedData(nums)
res0: Option[org.apache.spark.sql.execution.CachedData] =
Some(CachedData(Range (0, 5, step=1, splits=Some(8))
,InMemoryRelation [id#0L], true, 10000, StorageLevel(disk, memory, deserialized, 1 replicas)
+- *Range (0, 5, step=1, splits=8)
))