我需要了解如何连接具有数百万个数组的两个数据集。每个数据集的长数为1-10,000,000。但是每个人都有不同的分组。 [1,2] [3,4]和[1],[2,3],[4]输出应为[1,2,3,4] 我需要一些方法来有效地加入这些集合。
我尝试了一种方法,我多次爆炸和分组,最后排序和区分数组。这适用于小集合,但对于大集合非常无效,因为它会多次爆发行数。
有关如何使用其他方法(如reducer或聚合)更有效地解决此问题的任何想法。
以下是scala代码示例。但是,我需要一种适用于java的方法。
val rdd1 = spark.sparkContext.makeRDD(Array("""{"groupings":[1,2,3]}""", """{"groupings":[4,5,6]}""", """{"groupings":[7,8,9]}""", """{"groupings":[10]}""", """{"groupings":[11]}"""))
val rdd2 = spark.sparkContext.makeRDD(Array("""{"groupings":[1]}""", """{"groupings":[2,3,4]}""", """{"groupings":[7,8]}""", """{"groupings":[9]}""", """{"groupings":[10,11]}"""))
val srdd1 = spark.read.json(rdd1)
val srdd2 = spark.read.json(rdd2)
Dataset 1:
+---------+
|groupings|
+---------+
|[1, 2, 3]|
|[4, 5, 6]|
|[7, 8, 9]|
| [10]|
| [11]|
+---------+
Dataset 2:
+---------+
|groupings|
+---------+
| [1]|
|[2, 3, 4]|
| [7, 8]|
| [9]|
| [10, 11]|
+---------+
输出应为
+------------------+
| groupings|
+------------------+
|[1, 2, 3, 4, 5, 6]|
| [7, 8, 9]|
| [10, 11]|
+------------------+
更新: 这是我的原始代码,我运行时遇到了问题,@ AyanGuha让我想到,或许只使用一系列连接会更简单,我现在正在测试它,并且如果有效的话会发布一个解决方案。
srdd1.union(srdd2).withColumn("temp", explode(col("groupings")))
.groupBy("temp")
.agg(collect_list("groupings").alias("groupings"))
.withColumn("groupings", callUDF("distinctLongArray", callUDF("flattenDistinctLongArray", col("groupings"))))
.withColumn("temp", explode(col("groupings")))
.groupBy("temp")
.agg(collect_list("groupings").alias("groupings"))
.withColumn("groupings", callUDF("distinctLongArray", callUDF("flattenDistinctLongArray", col("groupings"))))
.withColumn("temp", explode(col("groupings")))
.groupBy("temp")
.agg(collect_list("groupings").alias("groupings"))
.withColumn("groupings", callUDF("distinctLongArray", callUDF("flattenDistinctLongArray", col("groupings"))))
.select(callUDF("sortLongArray", col("groupings")).alias("groupings"))
.distinct()
此代码显示的是,经过3次迭代后,数据合并,理想情况下,3个连接将执行相同的操作。
更新2: 看起来我有一个新的工作版本,仍然看起来效率低下但我认为这将由spark更好地处理。
val ardd1 = spark.sparkContext.makeRDD(Array("""{"groupings":[1,2,3]}""", """{"groupings":[4,5,6]}""", """{"groupings":[7,8,9]}""", """{"groupings":[10]}""", """{"groupings":[11,12]}""", """{"groupings":[13,14]}"""))
val ardd2 = spark.sparkContext.makeRDD(Array("""{"groupings":[1]}""", """{"groupings":[2,3,4]}""", """{"groupings":[7,8]}""", """{"groupings":[9]}""", """{"groupings":[10,11]}""", """{"groupings":[12,13]}""", """{"groupings":[14, 15]}"""))
var srdd1 = spark.read.json(ardd1)
var srdd2 = spark.read.json(ardd2)
val addUDF = udf((x: Seq[Long], y: Seq[Long]) => if(y == null) x else (x ++ y).distinct.sorted)
val encompassUDF = udf((x: Seq[Long], y: Seq[Long]) => if(x.size == y.size) false else (x diff y).size == 0)
val arrayContainsAndDiffUDF = udf((x: Seq[Long], y: Seq[Long]) => (x.intersect(y).size > 0) && (y diff x).size > 0)
var rdd1 = srdd1
var rdd2 = srdd2.withColumnRenamed("groupings", "groupings2")
for (i <- 1 to 3){
rdd1 = rdd1.join(rdd2, arrayContainsAndDiffUDF(col("groupings"), col("groupings2")), "left")
.select(addUDF(col("groupings"), col("groupings2")).alias("groupings"))
.distinct
.alias("rdd1")
rdd2 = rdd1.select(col("groupings").alias("groupings2")).alias("rdd2")
}
rdd1.join(rdd2, encompassUDF(col("groupings"), col("groupings2")), "leftanti")
.show(10, false)
输出:
+------------------------+
|groupings |
+------------------------+
|[10, 11, 12, 13, 14, 15]|
|[1, 2, 3, 4, 5, 6] |
|[7, 8, 9] |
+------------------------+
我会大规模地尝试这个,看看我得到了什么。
答案 0 :(得分:0)
这适用于小集,但对于大集非常无效,因为它会多次爆炸行数。
我认为除了explode
数组join
后面跟distinct
有其他选项。 Spark在这样的计算上相当不错,并尝试使用内部二进制行尽可能多地执行它们。压缩数据集,通常在字节级(JVM外部)进行比较
这只是一个足够的记忆力来容纳所有可能没什么大不了的元素。
我建议您试试解决方案并查看实际计划和统计数据。它最终可能成为唯一可用的解决方案。
答案 1 :(得分:0)
以下是使用作为HiveQL一部分支持的ARRAY数据类型的替代解决方案。这至少会使你的编码变得简单[即构建逻辑]。下面的代码假定原始数据位于文本文件中。
步骤1.创建表
create table array_table1(array_col1 array<int>) ROW FORMAT DELIMITED
FIELDS TERMINATED BY ',' COLLECTION ITEMS TERMINATED BY ',' LINES TERMINATED
BY'\n' STORED AS text;
第2步:将数据加载到两个表中
LOAD DATA INPATH('/path/to/file') OVERWRITE INTO TABLE array_table1
步骤3:应用sql函数获取结果
select distinct(explode(array_col1)) from array_table1 union
select distinct(explode(array_col2)) from array_table2
从示例中我不清楚您要查找的最终输出是什么。它只是所有不同数字的联合 - 或者它们是否应该进行分组?但无论如何,使用创建的表,您可以使用distinct,explode(),left anti join和union的组合来获得预期的结果。
您可能希望优化此代码,以便为重复项再次过滤最终数据集。
希望有所帮助!
答案 2 :(得分:0)
好的,我终于明白了。
首先,我的数组加入我做了一些非常错误的事情,我最初忽略了这一点。
连接具有等效性的两个数组时。 EX。 [1,2,3]等于[1,2,3]吗?数组是哈希的。我正在使用UDF进行交叉匹配。给定[1,2,3]中的x是[1,2,3,4,5]中的任何x。这不能进行哈希处理,因此需要一个计划来检查每一行的每一行。
要做到这一点,你必须首先爆炸两个数组,然后加入它们。 然后,您可以应用其他条件。例如,我通过仅连接不相等的数组来节省时间,并且总和的数量少于另一个。
自我加入的示例:
rdd2 = rdd2.withColumn("single", explode(col("grouping"))) // Explode the grouping
temp = rdd2.withColumnRenamed("grouping", "grouping2").alias("temp") // Alias for self join
rdd2 = rdd2.join(temp, rdd2.col("single").equalTo(temp.col("single")) // Compare singles which will be hashed
.and(col("grouping").notEqual(col("grouping2"))) // Apply further conditions
.and(callUDF("lessThanArray", col("grouping"), col("grouping2"))) // Make it so only [1,2,3] [4,5,6] is joined and not the duplicate [4,5,6] [1,2,3], for efficiency
, "left") // Left so that the efficiency criteria do not drop rows
然后我按照加入的分组进行分组,反对聚合来自自联接的分组。
rdd2.groupBy("grouping")
.agg(callUDF("list_agg",col('grouping2')).alias('grouping2')) // List agg is a UserDefinedAggregateFunction which aggregates lists into a distinct list
.select(callUDF("addArray", col("grouping"), col("grouping2")).alias("grouping")) // AddArray is a udf which concats and distincts 2 arrays
grouping grouping2
[1,2,3] [3,4,5]
[1,2,3] [2,6,7]
[1,2,3] [2,8,9]
becomes just
[1,2,3] [2,3,4,5,6,7,8,9]
after addArray
[1,2,3,4,5,6,7,8,9]
然后,我将代码重复了3次,这似乎可以使所有内容合并,并提供一个独特的好措施。
从原始问题中注意我有两个数据集,针对我的具体问题,我发现了关于第一组和第二组的一些假设。我可以假设的第一组没有重复,因为它是主列表,第二组有重复,因此我只需要将上面的代码应用到第二组,然后将其与第一组连接。我假设如果两个集合都有重复,那么它们可以先组合在一起。