Spark:对RDD中的高效质量查找

时间:2015-01-16 11:42:37

标签: scala apache-spark

在Apache Spark中我有两个RDD。包含键值形式的第一个data : RDD[(K,V)]数据。第二个pairs : RDD[(K,K)]包含一组有趣的数据密钥对。

如何有效地构建RDD pairsWithData : RDD[((K,K)),(V,V))] ,使其包含来自pairs的所有元素作为键元组及其对应的值(来自{{1 })作为value-tuple?

数据的某些属性:

  • data中的密钥是唯一的
  • data中的所有条目都是唯一的
  • 对于pairs中的所有对(k1,k2),保证pairs
  • '对'的大小只是数据k1 <= k2
  • 的大小
  • 当前数据大小(预计会增长):|pairs| = O(|data|)

当前尝试次数

以下是Scala中的一些示例代码:

|data| ~ 10^8, |pairs| ~ 10^10

尝试1

首先,我尝试在import org.apache.spark.rdd.RDD import org.apache.spark.SparkContext._ // This kind of show the idea, but fails at runtime. def massPairLookup1(keyPairs : RDD[(Int, Int)], data : RDD[(Int, String)]) = { keyPairs map {case (k1,k2) => val v1 : String = data lookup k1 head; val v2 : String = data lookup k2 head; ((k1, k2), (v1,v2)) } } // Works but is O(|data|^2) def massPairLookup2(keyPairs : RDD[(Int, Int)], data : RDD[(Int, String)]) = { // Construct all possible pairs of values val cartesianData = data cartesian data map {case((k1,v1),(k2,v2)) => ((k1,k2),(v1,v2))} // Select only the values who's keys are in keyPairs keyPairs map {(_,0)} join cartesianData mapValues {_._2} } // Example function that find pairs of keys // Runs in O(|data|) in real life, but cannot maintain the values def relevantPairs(data : RDD[(Int, String)]) = { val keys = data map (_._1) keys cartesian keys filter {case (x,y) => x*y == 12 && x < y} } // Example run val data = sc parallelize(1 to 12) map (x => (x, "Number " + x)) val pairs = relevantPairs(data) val pairsWithData = massPairLookup2(pairs, data) // Print: // ((1,12),(Number1,Number12)) // ((2,6),(Number2,Number6)) // ((3,4),(Number3,Number4)) pairsWithData.foreach(println) 上使用lookup函数,但在执行时会抛出运行时错误。似乎data特征中self为空。

此外,我不确定PairRDDFunctions的表现。 The documentation如果RDD通过仅搜索键映射到的分区而具有已知分区器,则此操作有效地完成。这听起来像lookup查找需要O(n * |分区|)时间充其量,我怀疑可以优化。

尝试2

这种尝试有效,但我会创建n对,这会破坏性能。我不希望Spark能够优化它。

1 个答案:

答案 0 :(得分:5)

您的查找1不起作用,因为您无法在工作人员内部执行RDD转换(在另一个转换中)。

在查找2中,我认为没有必要执行完整的笛卡尔...

你可以这样做:

val firstjoin = pairs.map({case (k1,k2) => (k1, (k1,k2))})
    .join(data)
    .map({case (_, ((k1, k2), v1)) => ((k1, k2), v1)})
val result = firstjoin.map({case ((k1,k2),v1) => (k2, ((k1,k2),v1))})
    .join(data)
    .map({case(_, (((k1,k2), v1), v2))=>((k1, k2), (v1, v2))})

或者以更密集的形式:

    val firstjoin = pairs.map(x => (x._1, x)).join(data).map(_._2)
    val result = firstjoin.map({case (x,y) => (x._2, (x,y))})
        .join(data).map({case(x, (y, z))=>(y._1, (y._2, z))})

我认为你不能更有效地做到这一点,但我可能错了......