我正在使用Scala
在Spark
中编写与图形相关的程序。数据集有400万个节点和400万个边缘(你可以把它当作一棵树),但每次(Iteration
),我只编辑它的一部分,即一个以给定为根的子树节点,以及给定节点和根之间的路径中的节点。
Iteration
具有相关性,这意味着i+1
Iteration
需要来自i
的结果。所以我需要存储每个Iteration
的结果用于下一步。
我试图找到更新RDD
的有效方法,但到目前为止还没有任何线索。我发现PairRDD
有一个lookup
函数可以减少计算量从O(N)
到O(M
)的时间,N
表示RDD
和M
中的对象总数表示每个分区中的元素数。
所以,我想我可以使用RDD
更新O(M)
中的对象吗?或者更理想的是,O(1)?(我在Spark的邮件列表中看到一封电子邮件,说lookup
可以修改为O(1))
另一件事是,如果我能够O(M)
更新RDD
,我可以将分区增加到比我拥有的核心数更大的数量并获得更好的性能吗?
答案 0 :(得分:6)
作为功能数据结构,RDD是不可变的,RDD上的操作会生成新的RDD。
结构的不可变性并不一定意味着完全复制。持久性数据结构是一种常见的功能模式,其中对不可变结构的操作产生新结构,但以前的版本被维护并经常被重用。
GraphX(Spark之上的'模块')是一个基于Spark的图形API,它使用了这样的概念:来自docs:
图表的值或结构的更改通过以下方式完成 生成具有所需更改的新图形。注意实质性 原始图形的一部分(即未受影响的结构,属性, 在新的图表中重复使用和标记,降低了成本 固有的功能数据结构。
这可能是解决手头问题的方法:http://spark.apache.org/docs/1.0.0/graphx-programming-guide.html
答案 1 :(得分:4)
RDD是分布式数据集,分区是RDD存储的单位,处理单元和RDD是元素。
例如,您从HDFS读取一个大文件作为RDD,然后该RDD的元素是String
(该文件中的行),并且spark通过分区将该RDD存储在整个群集中。对于你来说,作为一个火花用户,你只需要关心如何处理那些文件的行,就像你正在编写一个普通程序一样,并且你逐行从本地文件系统中读取一个文件。这就是火花的力量:)。
无论如何,您不知道哪个元素将存储在某个分区中,因此更新某个分区没有意义。
答案 2 :(得分:1)
MapReduce编程模型(和FP)并不真正支持单个值的更新。相反,人们应该定义一系列转换。
现在,如果你有相互依赖的值,即你不能用简单的map
执行转换,但需要聚合多个值并根据该值进行更新,那么你需要做的是考虑一种分组方式将这些值组合在一起然后转换每个组 - 或定义一个幺半群操作,以便可以将操作分配并切割成子步骤。
按方法分组
现在我会尝试针对您的具体案例提供更具体的信息。你说你有子树,是否可以先将每个节点映射到一个指示相应子树的键?如果是这样,你可以这样做:
nodes.map(n => (getSubTreeKey(n), n)).grouByKey().map ...
<强>含半幺群强>
(严格来说,你想要一个可交换的幺半群)最好你读http://en.wikipedia.org/wiki/Monoid#Commutative_monoid
例如+
是一个单字节操作,因为当一个人想要计算Ints的RDD之和时,那么底层框架可以将数据分成块,在每个块上执行求和,然后总结得到的总和(可能只有两步以上)。如果你能找到一个最终会产生单一更新所需结果的幺半群,那么你就有办法分发你的处理。 E.g。
nodes.reduce(_ myMonoid _)