spark shuffle写的超级慢

时间:2018-06-11 19:14:28

标签: apache-spark apache-spark-sql

为什么火花洗牌阶段对于1.6MB shuffle写入和2.4MB输入来说是如此之慢?。同样为什么洗牌只在一个执行器上发生??我正在运行一个3节点集群,每个集群有8个核心。 / p>

Spark UI:

enter image description here enter image description here 代码:

*JavaPairRDD<String, String> javaPairRDD = c.mapToPair(new PairFunction<String, String, String>() {
    @Override
    public Tuple2<String, String> call(String arg0) throws Exception {
        // TODO Auto-generated method stub

        try {
            if (org.apache.commons.lang.StringUtils.isEmpty(arg0)) {
                return new Tuple2<String, String>("", "");
            }
            Tuple2<String, String> t = new Tuple2<String, String>(getESIndexName(arg0), arg0);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("******* exception in getESIndexName");
        }
        return new Tuple2<String, String>("", "");
    }
});

java.util.Map<String, Iterable<String>> map1 = javaPairRDD.groupByKey().collectAsMap();* 

1 个答案:

答案 0 :(得分:1)

为什么shuffle只在一个执行器上发生:

请检查您的RDD分区,按照UI图像帮助您查找

enter image description here

我认为您的RDD只有一个分区,而不是8个或更多分区,最终将使用所有执行程序。

rdd = rdd.repartition(8) 

<强> Avoiding Shuffle "Less stage, run faster

Shuffling是一个跨分区重新分配数据(也称为重新分区)的过程,这些过程可能会或可能不会导致跨JVM进程或甚至通过线路移动数据(在不同计算机上的执行程序之间)。

默认情况下,shuffling不会改变分区数,因为你只有一个分区看起来很慢。

如何避免随机播放:

  • 当两个RDD都有重复键时,连接可能会导致数据大小急剧​​扩大。执行distinct或combineByKey操作以减少密钥空间或使用cogroup处理重复密钥而不是生成完整的交叉产品可能更好。通过在组合步骤中使用智能分区,可以防止连接中的第二次混洗(稍后我们将详细讨论)。

  • 如果两个RDD中都没有密钥,则可能会意外丢失数据。使用外连接可能更安全,因此您可以保证将所有数据保留在左侧或右侧RDD中,然后在连接后过滤数据。

  • 如果一个RDD有一些易于定义的密钥子集,在另一个RDD中你可能最好在加入之前进行过滤或减少,以避免大量的数据混乱,无论如何你最终会丢弃这些数据

  • 为了加入数据,Spark需要将要连接的数据(即基于每个键的数据)存放在同一个分区上。 Spark中的连接的默认实现是混乱的散列连接。混洗散列连接通过使用与第一个数据集相同的默认分区器对第二个数据集进行分区来确保每个分区上的数据将包含相同的键,以便来自两个数据集的具有相同散列值的键位于同一分区中。虽然这种方法总是有效,但它可能比必要的更昂贵,因为它需要随机播放。如果出现以下情况,可以避免洗牌:

    1.两个RDD都有一个已知的分区器。

    1. 其中一个数据集足够小以适应内存,在这种情况下我们可以进行广播散列连接(我们将在后面解释这个)。
  

请注意,如果RDD共处,则可以进行网络传输   避免,随着洗牌。重新分区后始终坚持

  • DataFrame联接DataFrame之间的连接数据是最常见的多数据框架转换之一。标准SQL连接类型都受支持,并且在执行连接时可以在df.join(otherDf,sqlCondition,joinType)中指定为joinType。与RDD之间的连接一样,使用非唯一键连接将产生交叉乘积(因此,如果左表有R1和R2与key1,右表有R3,R5与key1,你将获得(R1,R3),(R1, R5),(R2,R3),(R2,R5))在输出中。

  • 使用自连接和点亮(true),您可以生成数据集的笛卡尔积,这可能很有用,但也说明了连接(尤其是自连接)如何容易导致数据量不可行。< / p>

  • 使用广播联接的广播联接,通过避免通过网络发送大表的所有数据,可以非常有效地加入具有相对较小的表(维度)的大表(事实)。您可以使用广播功能在连接运算符中使用时标记要广播的数据集。它使用spark.sql.autoBroadcastJoinThreshold设置来控制在执行连接时将广播到所有工作节点的表的大小。

  • 使用相同的分区程序。如果两个RDD具有相同的分区,则连接不会导致混洗。但请注意,缺少shuffle并不意味着不必在节点之间移动数据。两个RDD可能具有相同的分区(被共同分区),但是具有位于不同节点上的相应分区(不是共同定位的)。这种情况仍然比洗牌更好,但要记住这一点。共址可以提高性能,但很难保证。

  • 如果数据量很大和/或您的群集无法增长甚至导致OOM,请使用两遍方法。首先,重新分区数据并使用分区表(dataframe.write.partitionBy())持久化。然后,在循环中串行连接子分区,&#34;追加&#34;到同一个最终结果表。

  • https://www.slideshare.net/cloudera/top-5-mistakes-to-avoid-when-writing-apache-spark-applications

  • https://medium.com/@foundev/you-won-t-believe-how-spark-shuffling-will-probably-bite-you-also-windowing-e39d07bf754e
  • https://blog.cloudera.com/blog/2015/01/improving-sort-performance-in-apache-spark-its-a-double/ - https://jaceklaskowski.gitbooks.io/mastering-apache-spark/spark-rdd-shuffle.html