Spark的groupByKey替代方案

13
根据Databricks的最佳实践,应避免使用Spark的groupByKey。Spark的groupByKey处理方式是先将信息在工作节点之间进行洗牌,然后再进行处理。解释 所以,我的问题是,有哪些替代方法可以以分布式和快速的方式返回以下内容?
// want this
{"key1": "1", "key1": "2", "key1": "3", "key2": "55", "key2": "66"}
// to become this
{"key1": ["1","2","3"], "key2": ["55","66"]}

似乎可以使用aggregateByKeyglom在分区中先执行(map),然后将所有列表连接在一起(reduce)来完成此操作。

以下是更适合使用的比 groupByKey 更好的函数:当您要合并元素但返回类型与输入值类型不同时,可以使用 combineByKey。 foldByKey 使用关联函数和中立的“零值”合并每个键的值。 - Abhishek Choudhary
我认为groupByKey是这里最有效的选择(无论是时间还是存储)。如果它OOMs,你只需要一个更大的集群。 - shuaiyuancn
1个回答

18

groupByKey适用于我们想要“较小”值集合的情况,如问题所述。

TL;DR

groupByKey的“不要使用”警告适用于两种一般情况:

1)您想聚合值:

  • 不要:rdd.groupByKey().mapValues(_.sum)
  • 使用:rdd.reduceByKey(_ + _)

在这种情况下,groupByKey会浪费资源来实现一个集合,而我们想要的是单个元素作为答案。

2)您想对低基数键上的非常大的集合进行分组:

  • 不要:allFacebookUsersRDD.map(user => (user.likesCats, user)).groupByKey()
  • 绝对不要

在这种情况下,groupByKey可能会导致OOM错误。

groupByKey将一个执行器中同一键的所有值材料化为一个集合。正如提到的,它有内存限制,因此根据情况其他选项更好。

所有分组函数(例如groupByKeyaggregateByKeyreduceByKey)都依赖于基础:combineByKey,因此对于问题的用例,没有其他更好的选择,它们都依赖于相同的通用过程。


那么,没有办法根据键将数组中的值分组,每个值在自己的工作器中,然后将结果数组与其他部分数组合并吗?我的问题是为了创建一个反向索引,例如,我会在数组中拥有页面中的所有单词,并将键设置为URL。 - Adriano Almeida
@AdrianoAlmeida 是的 - groupByKey :-)。请阅读我在答案中提供的链接:https://github.com/apache/spark/blob/43f50decdd20fafc55913c56ffa30f56040090e4/core/src/main/scala/org/apache/spark/rdd/PairRDDFunctions.scala#L462。他们建议不要使用 map-side combiner,因为会影响内存。如果您认为您的特定用例首先从 map-side combine 中受益,则可以尝试使用 aggregateByKey - maasg
@massg 这是否意味着如果我们使用CassandraSQLContext的以下查询:Select Column1, max(Column2) from Table_name Group by Column1,也会影响性能?如果是的话,有什么替代方法可以将其转换为reduceby? - Naresh
2
处理reduce操作不满足结合律的最佳方法是什么?也就是说,如果我需要对键进行分组,对分组进行排序,然后对排序后的列表应用函数,那么我不能使用reduceByKey。我是否应该使用partitionBy + mapPartitions来代替groupByKey + mapValues以避免OOM错误?每个键创建一个分区会有问题吗? - Bob Baxley
很棒的答案!非常简洁。 - guilhermecgs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接