平面图内的Spark map可以复制笛卡尔连接

时间:2015-09-25 13:14:40

标签: scala apache-spark rdd

我正在尝试模糊连接两个数据集,其中一个引号和一个销售。为了论证,加入属性是名字,姓氏,dob和电子邮件。

我有26m +报价和1m +销售。客户可能没有使用一个或多个属性的准确信息,因此我给每个匹配的分数(1,1,1,1),其中所有匹配(0,0,0,0),其中没有匹配。

所以我最终得到类似于

的东西
q1, s1, (0,0,1,0)
q1, s2, (0,1,0,1)
q1, s3, (1,1,1,1)
q2, s1, (1,0,0,1)
...
q26000000 s1 (0,1,0,0)

所以我认为这相当于我正在管理我为报价制作大量分区的笛卡尔产品

val quotesRaw = sc.textfile(....)
val quotes = quotesRaw.repartition(quotesRaw.count().toInt() / 100000)

val sales = sc.textfile(...)
val sb = sc.broadcast(sales.collect())

quotes.mapPartitions(p=> (
   p.flatMap(q => (
      sb.value.map(s =>
          q._1, s._1, ( if  q._2 == s._2 1 else 0, etc)
   )
)

如果我将数字保持在较低水平,例如2600万的报价,但只有1000个销售,但如果我运行它将会在运行时停止响应所有销售

我正在使用以下配置运行它。

spark-submit --conf spark.akka.frameSize=1024 --conf spark.executor.memory=3g --num-executors=30 --driver-memory 6g --class SalesMatch --deploy-mode client --master yarn SalesMatching-0.0.1-SNAPSHOT.jar hdfs://cluster:8020/data_import/Sales/SourceSales/2014/09/01/SourceSales_20140901.txt hdfs://cluster:8020/data_import/CDS/Enquiry/2014/01/01/EnquiryBackFill_20140101.txt hdfs://cluster:8020/tmp/_salesdata_matches_new

这里有什么东西突然显示不正确吗?

1 个答案:

答案 0 :(得分:1)

假设每个分区有100k引号和总大小为40MB的11M销售,那么你的代码每个分区产生大约4TB的数据,所以你的工作人员不太可能处理这个问题而且绝对不能在内存中完成。

我认为你只对近距离比赛感兴趣,所以有必要尽早过滤。稍微简化你的代码(据我所知,没有理由使用mapPartitions):

// Check if match is close enough, where T is type of (q._1, s._1, (...))
def isCloseMatch(match: T): Boolean = ??? 

quotes.flatMap(q => sb.value
  .map(s => (q._1, s._1, (....))) // Map as before
  .filter(isCloseMatch) // yield only close matches
)

一般评论:

  • 从RDD创建广播是一个昂贵的过程。首先,您必须将所有数据传输到驱动程序,然后在工作人员之间分配。它意味着重复的序列化/反序列化,网络流量和存储数据的成本
  • 对于像这样的相对简单的操作,使用高级Spark SQL API可能是个好主意:

    import org.apache.spark.sql.DataFrame
    
    val salesDF: DataFrame = ???
    val salesDF: DataFrame = ???
    val featureCols: Seq[String] = ???
    val threshold: Int = ???
    
    val inds = featureCols // Boolean columns 
      .map(col => (quotesDF(col) === salesDF(col)).alias(s"${col}_ind"))
    
    val isSimilar = inds // sum(q == s) > threshold
      .map(c => c.cast("integer").alias(c.toString))
      .reduce(_ + _)
      .geq(threshold)
    
    val combined = quotesDF
        .join(salesDF, isSimilar, "left")