Algorithm 根据数据完成基于RDD的RDD

Algorithm 根据数据完成基于RDD的RDD,algorithm,scala,apache-spark,rdd,Algorithm,Scala,Apache Spark,Rdd,我在warn集群上使用spark2.1。我有一个RDD,其中包含我希望基于其他RDD完成的数据(对应于我通过的不同mongo数据库,但我认为这并不重要,只是以防万一) 我的问题是,我必须使用的RDD来完成数据取决于数据本身,因为数据包含要使用的数据库。以下是我必须做的一个简化示例: /* * The RDD which needs information from databases */ val RDDtoDevelop = sc.parallelize(Array( Map("d

我在
warn
集群上使用
spark2.1
。我有一个
RDD
,其中包含我希望基于其他
RDD
完成的数据(对应于我通过的不同
mongo
数据库,但我认为这并不重要,只是以防万一)

我的问题是,我必须使用的
RDD
来完成数据取决于数据本身,因为数据包含要使用的
数据库。以下是我必须做的一个简化示例:

/*
 * The RDD which needs information from databases
 */
val RDDtoDevelop = sc.parallelize(Array(
    Map("dbName" -> "A", "id" -> "id1", "other data" -> "some data"),
    Map("dbName" -> "C", "id" -> "id6", "other data" -> "some other data"),
    Map("dbName" -> "A", "id" -> "id8", "other data" -> "some other other data")))
    .cache()

/*
 * Artificial databases for the exemple. Actually, mongo-hadoop is used. https://github.com/mongodb/mongo-hadoop/wiki/Spark-Usage 
 * This means that generate these RDDs COSTS so we don't want to generate all possible RDDs but only needed ones
 */ 
val A = sc.parallelize(Array(
    Map("id" -> "id1", "data" -> "data1"),
    Map("id" -> "id8", "data" -> "data8")
    ))

val B = sc.parallelize(Array(
    Map("id" -> "id1", "data" -> "data1bis"),
    Map("id" -> "id5", "data" -> "data5")
    ))

val C = sc.parallelize(Array(
    Map("id" -> "id2", "data" -> "data2"),
    Map("id" -> "id6", "data" -> "data6")
    ))

val generateRDDfromdbName = Map("A" -> A, "B" -> B, "C" -> C)
想要的结果是:

Map(dbName -> A, id -> id8, other data -> some other other data, new data -> data8)
Map(dbName -> A, id -> id1, other data -> some data, new data -> data1)
Map(dbName -> C, id -> id6, other data -> some other data, new data -> data6)
由于嵌套的
RDD
s是不可能的,因此我想找到尽可能使用
Spark
并行的最佳方法。我考虑了两种解决方案

首先是使用所需数据库的内容创建一个集合,然后将其转换为
RDD
,以获得
RDD
可伸缩性(如果集合不适合
驱动程序内存
,我可以多次这样做)。最后,对
id
上的内容执行
join
filter
操作

第二种方法是从所有需要的
数据库中获取
RDD
,通过
dbname
id
键入它们,然后执行
加入

下面是scala的
代码:

解决方案1 解决方案2 它们都给出了想要的输出。在我看来,第二个似乎更好,因为
db
id
的匹配使用了
Spark
的平行性,但我不确定这一点。你能帮我选择最好的,甚至更好的,给我一些线索,找到比地雷更好的解决方案吗

任何其他评论都非常感谢(这是我在网站上的第一个问题;))

提前谢谢你


Matt

我建议您将
RDD
转换为
数据帧
s,然后
连接
不同的
和您希望应用于数据的其他
函数

数据帧
是分布式的,除了
数据帧API
,还可以使用
sql查询
。更多信息可在和中找到
此外,您不需要使用使代码运行缓慢的
foreach
collect
函数
将RDDtoDevelop转换为dataframe的示例如下

val RDDtoDevelop = sc.parallelize(Array(
  Map("dbName" -> "A", "id" -> "id1", "other data" -> "some data"),
  Map("dbName" -> "C", "id" -> "id6", "other data" -> "some other data"),
  Map("dbName" -> "A", "id" -> "id8", "other data" -> "some other other data")))
  .cache()
将上述
RDD
转换为
dataFrame

val developColumns=RDDtoDevelop.take(1).flatMap(map=>map.keys)

val developDF = RDDtoDevelop.map{value=>
  val list=value.values.toList
  (list(0),list(1),list(2))
}.toDF(developColumns:_*)
数据帧
如下所示

+------+---+---------------------+
|dbName|id |other data           |
+------+---+---------------------+
|A     |id1|some data            |
|C     |id6|some other data      |
|A     |id8|some other other data|
+------+---+---------------------+
A
rdd
转换为
dataframe
如下
A的源代码:

val A = sc.parallelize(Array(
  Map("id" -> "id1", "data" -> "data1"),
  Map("id" -> "id8", "data" -> "data8")
))
DataFrame
A的代码:

val aColumns=A.take(1).flatMap(map=>map.keys)

val aDF = A.map{value =>
  val list=value.values.toList
  (list(0),list(1))
}.toDF(aColumns:_*).withColumn("name", lit("A"))
一个新的列
名称
数据库名称
一起添加,以便在末尾与
developpdf
一起加入正确的
join

DataFrame
A的输出:

+---+-----+----+
|id |data |name|
+---+-----+----+
|id1|data1|A   |
|id8|data8|A   |
+---+-----+----+
您可以用类似的方式转换
B
C

B的来源:

val B = sc.parallelize(Array(
  Map("id" -> "id1", "data" -> "data1bis"),
  Map("id" -> "id5", "data" -> "data5")
))
B的数据帧:

    val bColumns=B.take(1).flatMap(map=>map.keys)

    val bDF = B.map{value =>
      val list=value.values.toList
      (list(0),list(1))
    }.toDF(bColumns:_*).withColumn("name", lit("B"))
B的输出:

+---+--------+----+
|id |data    |name|
+---+--------+----+
|id1|data1bis|B   |
|id5|data5   |B   |
+---+--------+----+
C的来源:

val C = sc.parallelize(Array(
  Map("id" -> "id2", "data" -> "data2"),
  Map("id" -> "id6", "data" -> "data6")
))
C的数据帧代码:

val cColumns=C.take(1).flatMap(map=>map.keys)

val cDF = C.map{value =>
  val list=value.values.toList
  (list(0),list(1))
}.toDF(cColumns:_*).withColumn("name", lit("C"))
C的输出:

+---+-----+----+
|id |data |name|
+---+-----+----+
|id2|data2|C   |
|id6|data6|C   |
+---+-----+----+
转换后,
A
B
C
可以使用
union

var unionDF = aDF.union(bDF).union(cDF)
那是什么

+---+--------+----+
|id |data    |name|
+---+--------+----+
|id1|data1   |A   |
|id8|data8   |A   |
|id1|data1bis|B   |
|id5|data5   |B   |
|id2|data2   |C   |
|id6|data6   |C   |
+---+--------+----+
然后,在
unionDF
id
列的
重命名之后,它只是加入
developpdf
unionDF
,以便以后将其删除

unionDF = unionDF.withColumnRenamed("id", "id1")
unionDF = developDF.join(unionDF, developDF("id") === unionDF("id1") && developDF("dbName") === unionDF("name"), "left").drop("id1", "name")
终于有了

+------+---+---------------------+-----+
|dbName|id |other data           |data |
+------+---+---------------------+-----+
|A     |id1|some data            |data1|
|C     |id6|some other data      |data6|
|A     |id8|some other other data|data8|
+------+---+---------------------+-----+
之后你可以做需要的事。
注意:
lit
功能将与以下导入一起使用

import org.apache.spark.sql.functions._

对于这两个阶段,我们谈论的数据大小是多少?@stefanobaghino目前只有几Mb,因为它还没有投入生产,但其目的是根据数据集的实际大小、集群内的计算能力和带宽来处理一些GB的数据,你甚至可以考虑收集你所需要的数据并进行广播。我碰巧广播了GBs,但没有太大的麻烦(我们正在讨论通过集群上的广播集过滤TBs,该集群上有一条连接节点的10Gbps线路)。我还建议检查Spark是否真的是您所需要的,特别是如果整个数据集小于10GB,商品服务器完全可以掌握。是的,我们知道Spark的使用情况有限,但下一步是使用Spark的机器学习库进行一些复杂的分析。我们希望长大,这样我们将有越来越多的数据需要管理。谢谢你的回答,数据帧看起来确实很有趣。但它是否适合NoSQL数据格式?我认为这可以绕过创建可为空的字段。这并不能解决需要先获得所需数据库的名称,然后生成相应的数据帧来将它们合并的问题,我错了吗?现在,我将尝试按照您的建议使用数据帧来调整我的第二个解决方案,这确实可以改进它并使其更容易,直到我找到更好的方法为止。我已经更新了我的答案,并使您和其他人更容易遵循。您可以在数据帧中使用sql查询。如果A、B和C是来自同一个流式mongo数据库的数据,那么最好将它们放在一个数据帧中,但是如果它们来自不同的源,那么是的,您需要一个联合。代码中的问题是您联合数据库数据而不提供源,请参见输出中的,data1bis已被联合,但不应该。但我明白了。谢谢你的时间和帮助。@Matt,我已经更新了我的答案,应该能满足你的需要。我很高兴能为您提供一些想法供您参考。:)
+------+---+---------------------+-----+
|dbName|id |other data           |data |
+------+---+---------------------+-----+
|A     |id1|some data            |data1|
|C     |id6|some other data      |data6|
|A     |id8|some other other data|data8|
+------+---+---------------------+-----+
import org.apache.spark.sql.functions._