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._