Java (Spark skewed join)如何连接两个具有高度重复密钥的大型Spark RDD,而不会出现内存问题?
在中,我试图通过SparkJava (Spark skewed join)如何连接两个具有高度重复密钥的大型Spark RDD,而不会出现内存问题?,java,apache-spark,join,rdd,scalability,Java,Apache Spark,Join,Rdd,Scalability,在中,我试图通过Sparkjoin避免使用join来避免内存问题 在这个新问题中,我使用了join,但试图用它解决内存问题 这是我的两个RDD: productToCustomerRDD: 大小:非常大,可能有数百万个不同的键 使用HashPartitioner对键进行分区 有些键将被高度复制,有些则不会 (toast, John) (butter, John) (toast, Jane) (jelly, Jane) productToCountRDD: 大小:非常大,可能有数百万个不同的键
join避免使用join
来避免内存问题
在这个新问题中,我使用了join
,但试图用它解决内存问题
这是我的两个RDD:
productToCustomerRDD:
大小:非常大,可能有数百万个不同的键
使用HashPartitioner对键进行分区
有些键将被高度复制,有些则不会
(toast, John)
(butter, John)
(toast, Jane)
(jelly, Jane)
productToCountRDD:
大小:非常大,可能有数百万个不同的键,太大而无法播放
使用HashPartitioner对键进行分区
键是唯一的,值是购买该产品的客户数
(toast, 2)
(butter, 1)
(jelly, 1)
我想加入这两个RDD,结果将是:
customerToProductAndCountRDD:
(toast, (John, 2))
(butter, (John, 1))
(toast, (Jane, 2))
(jelly, (Jane, 1))
如果我用productToCustomerRDD.join(productToCountRDD)
连接两个RDD,我会在两个分区上得到一个OutOfMemoryError
(共数千个分区)。在Spark UI中,我注意到在包含join
的阶段,在Input Size/Records
列中,所有分区都有从4K到700K的大量记录。除了生成OOM的两个分区之外,所有分区都有:一个分区有9M记录,另一个分区有6M记录
据我所知,为了加入,具有相同密钥的对需要被洗牌并移动到相同的分区(除非它们以前是按密钥分区的)。但是,由于某些键非常频繁(例如:数据集中几乎每个客户都购买的产品),因此在连接期间或在连接之前的重新分区期间,可能会将大量数据移动到一个分区
我理解正确吗?
有没有办法避免这种情况?
有没有一种方法可以连接,而不必在同一分区上拥有一个重复制键的所有数据 我的第一个问题是:你真的需要这些详细的数据吗?你真的需要知道约翰买了2只蟾蜍等等吗?我们处于大数据环境中,处理大量数据,因此有时聚合是减少基数并在分析和性能方面获得良好结果的一件好事。因此,如果您想知道一个产品的销售次数,您可以使用pairdd(产品,计数)[这样,每个产品都有一个元素],或者如果您想知道用户偏好,您可以使用pairdd(用户,购买产品列表)[这样,每个用户都有一个元素]。如果您真的需要知道toast是从Jhon购买的,为什么要将toast键拆分为不同的重新分区?这样,您就无法计算全局结果,因为在每个chunck中,您只有一条关于键的信息。实际上,这是Spark中的一个标准问题,称为“歪斜连接”:连接的一侧歪斜,这意味着它的一些键比其他键更频繁。可以找到一些不适合我的答案
我使用的策略受到了GraphFrame.skewedJoin()
方法的启发,该方法是在ConnectedComponents.skewedJoin()中定义的,并在ConnectedComponents.skewedJoin()中使用的。
连接将通过使用广播连接最频繁的键和使用标准连接较不频繁的键来执行
在我的示例(OP)中,producttocountrd
已经包含了有关键频率的信息。
所以它是这样的:
- 过滤
productToCountRDD
以仅保留高于固定阈值的计数,并collectamap()
将其发送给驱动程序
- 将此地图广播给所有执行者
- 将
productToCustomerRDD
拆分为两个RDD:在广播映射中找到的键(频繁键)和不在广播映射中的键(不频繁键)
- 使用
mapToPair
执行频繁键的联接,从广播映射中获取计数
- 使用
join
执行不常用键的联接
- 最后使用
union
获取完整的RDD