Apache Spark - shuffle写入的数据多于输入数据

时间:2017-05-18 22:42:41

标签: apache-spark shuffle

我在本地模式下使用Spark 2.1,我正在运行这个简单的应用程序。

val N = 10 << 20

sparkSession.conf.set("spark.sql.shuffle.partitions", "5")
sparkSession.conf.set("spark.sql.autoBroadcastJoinThreshold", (N + 1).toString)
sparkSession.conf.set("spark.sql.join.preferSortMergeJoin", "false")

val df1 = sparkSession.range(N).selectExpr(s"id as k1")
val df2 = sparkSession.range(N / 5).selectExpr(s"id * 3 as k2")

df1.join(df2, col("k1") === col("k2")).count()

此处,范围(N)创建 Long 的数据集(具有唯一值),因此我假设

的大小
  
      
  • df1 = N * 8字节~80MB
  •   
  • df2 = N / 5 * 8字节~16MB
  •   

好的,现在让我们以df1为例。 df1由8个分区 shuffledRDDs 5 组成,所以我假设

  
      
  • mappers(M)= 8
  •   
  • #of redurs(R)= 5
  •   

由于分区数量很少,Spark会使用Hash Shuffle,它会在磁盘中创建 M * R文件,但我还不知道每个文件是否都包含所有数据,因此< strong> each_file_size = data_size 产生 M * R * data_size 文件或 all_files = data_size

然而,当执行此应用程序时,随机写入 df1 = 160MB ,这与上述任何一种情况都不匹配。

Spark UI

我在这里缺少什么?为什么shuffle写入数据的大小加倍?

1 个答案:

答案 0 :(得分:4)

首先,让我们看看data size total(min, med, max)的含义:

根据SQLMetrics.scala#L88ShuffleExchange.scala#L43,我们看到的data size total(min, med, max)是shuffle dataSize指标的最终值。然后,它是如何更新的?每次序列化记录时都会更新:dataSize.add(row.getSizeInBytes) UnsafeRowUnsafeRow是Spark SQL中记录的内部表示)。

在内部,byte[]getSizeInBytes()支持,并在序列化期间直接复制到基础输出流,其byte[]方法只返回long的长度。因此,最初的问题转换为:为什么字节表示是记录的唯一UnsafeRow列的两倍大?这个UnsafeRowSerializer.scala#L66文档给了我们答案:

  

每个元组有三个部分:[空位设置] [值] [可变长度部分]

     

位集用于空值跟踪,并与8字节字边界对齐。它每个字段存储一位。

因为它的8字节字对齐,所以只有1个空位占用另一个8字节,宽度与长列相同。因此,每个var utcSeconds = 1234567890; var d = new Date(0); // The 0 there is the key, which sets the date to the epoch d.setUTCSeconds(utcSeconds); 代表使用16个字节的一长列行。