为什么repartition()方法会增加磁盘上的文件大小?

时间:2019-01-16 13:22:07

标签: apache-spark

我正在使用的数据湖(df)具有2 TB的数据和20,000个文件。我想将数据集压缩为2,000个1 GB文件。

如果运行df.coalesce(2000)并写出到磁盘,则数据湖将包含1.9 TB的数据。

如果运行df.repartition(2000)并将其写出到磁盘,则数据湖将包含2.6 TB的数据。

repartition()数据湖中的每个文件正好比预期大0.3 GB(它们都是1.3 GB,而不是1 GB)。

为什么repartition()方法会增加整个数据湖的大小?

a related question讨论了为什么运行聚合后数据湖的大小会增加。答案是:

  

通常,像Parquet这样的列式存储格式在数据分配(数据组织)和单个列的基数方面非常敏感。数据越有条理,基数越低,存储效率越高。

coalesce()算法是否提供了更有条理的数据...我认为不是...

我不认为其他问题能回答我的问题。

1 个答案:

答案 0 :(得分:11)

免责声明

此答案主要包含推测。对于这种现象的详细解释可能需要对输入和输出(或至少它们各自的元数据)进行深入分析。

观察

  1. 熵有效地限制了可能的最强无损压缩的性能-Wikipedia - Entropy (information theory)
  2. persistent columnar formatsthe internal Spark SQL representation都透明地应用了不同的压缩技术(例如Run-length encodingdictionary encoding)来减少存储数据的内存占用。 / p>

    另外,可以使用通用压缩算法对磁盘格式(包括纯文本数据)进行显式压缩-目前尚不清楚这种情况。

  3. 压缩(显式或透明)应用于数据块(通常是分区,但可以使用较小的单位)。

  4. 基于1),2)和3),我们可以假设平均压缩率将取决于群集中数据的分布。我们还应注意,如果上游谱系包含广泛的转化,则最终结果可能是不确定的。

coalescerepartition的可能影响

通常 coalesce 可以采用两条路径:

  • 通过管道升级到源头-最常见的情况。
  • 传播到最近的随机播放。

在第一种情况下,我们可以预期压缩率将与输入的压缩率相当。但是,在某些情况下可以实现更小的最终输出。让我们想象一个退化的数据集:

val df = sc.parallelize(
  Seq("foo", "foo", "foo", "bar", "bar", "bar"),
  6 
).toDF

如果将这样的数据集写入磁盘,则没有压缩的可能-每个值都必须原样写入:

df.withColumn("pid", spark_partition_id).show
+-----+---+
|value|pid|
+-----+---+
|  foo|  0|
|  foo|  1|
|  foo|  2|
|  bar|  3|
|  bar|  4|
|  bar|  5|
+-----+---+

换句话说,我们需要大约6 * 3个字节,总共18个字节。

但是如果我们合并

df.coalesce(2).withColumn("pid", spark_partition_id).show
+-----+---+
|value|pid|
+-----+---+
|  foo|  0|
|  foo|  0|
|  foo|  0|
|  bar|  1|
|  bar|  1|
|  bar|  1|
+-----+---+

例如,我们可以将int较小的RLE应用于计数,并将每个分区存储3 +1字节,总共8字节。

这当然是一个极大的简化,但是显示了保持低熵输入结构以及合并块如何可以减少内存占用。

第二种coalesce场景不太明显,但是在某些场景中,可以通过上游处理来减少熵(例如,考虑有关窗口函数的信息),并且保留这​​种结构将是有益的。

repartition 怎么样?

在没有分区表达式的情况下,repartition适用于RoundRobinPartitioning(使用基于分区ID的伪随机密钥实现为HashPartitioning)。只要散列函数的行为合理,这种重新分配就应该使数据的熵最大化,从而降低可能的压缩率。

结论

coalesce不应仅提供任何特定的好处,而应保留数据分发的现有属性-该属性在某些情况下可能是有利的。

repartition由于其性质,平均而言会使情况变得更糟,除非数据的熵已经最大化(这种情况可能会有所改善,但在非平凡的数据集上极不可能发生)。

repartition >

最后repartitionByRange带有分区表达式或{{1}}应该减少熵并提高压缩率。

注意

我们还应该记住,列格式通常基于运行时统计信息来决定特定的压缩/编码方法(或缺乏压缩方法)。因此,即使特定块中的行集合是固定的,但是行的顺序发生了变化,我们也可以观察到不同的结果。