Apache spark 为什么火花行对象与等效结构相比如此大?

Apache spark 为什么火花行对象与等效结构相比如此大?,apache-spark,Apache Spark,我一直在玩javasizeoflibrary(),并用它来测量apachespark中的数据集大小。事实证明,行对象大得离谱。像是巨大的——为什么 以一个相当简单的模式为例: root |-- account: string (nullable = true) |-- date: long (nullable = true) |-- dialed: string (nullable = true) |-- duration: double (nullable = true) 示例数据如

我一直在玩
javasizeof
library(),并用它来测量apachespark中的数据集大小。事实证明,
对象大得离谱。像是巨大的——为什么

以一个相当简单的模式为例:

root
 |-- account: string (nullable = true)
 |-- date: long (nullable = true)
 |-- dialed: string (nullable = true)
 |-- duration: double (nullable = true)
示例数据如下所示:

+-------+-------------+----------+--------+
|account|         date|    dialed|duration|
+-------+-------------+----------+--------+
|   5497|1434620384003|9075112643|   790.0|
+-------+-------------+----------+--------+
SizeEstimator.estimate(df.take(1000))
// res21: Long = 850711696
所以现在我们做:

val row = df.take(1)(0)
// row: org.apache.spark.sql.Row = [5497,1434620384003,9075112643,790.0]
所以现在我使用
SizeEstimator

SizeEstimator.estimate(row)
// res19: Long = 85050896
81兆字节!一排!认为这是某种错误,我确实:

SizeEstimator.estimate(df.take(100))
// res20: Long = 85072696
有趣的是,它并没有大多少——尽管拥有的数据量是它的100倍,但只比它大约20k。在100以上,它似乎是线性的。对于1000行,它如下所示:

+-------+-------------+----------+--------+
|account|         date|    dialed|duration|
+-------+-------------+----------+--------+
|   5497|1434620384003|9075112643|   790.0|
+-------+-------------+----------+--------+
SizeEstimator.estimate(df.take(1000))
// res21: Long = 850711696
好的,这大约是100行的10倍——或多或少是线性的。从测试中,它以线性方式增加,持续超过100行。根据这些测试,在大约100行之后,每行对象的成本仍然超过800 KB

出于好奇,我对相同的底层数据尝试了几种不同的对象类型。例如,以下是
数组
对象而不是
对象的
数组
的结果:

SizeEstimator.estimate(
  df.map(r => (r.getString(0), r.getLong(1), r.getString(2), r.getDouble(3))).take(1)
)
// res22: Long = 216
好的,这样好一点。更好的是,对于10行,它只有1976字节,而对于100行,它只有19616字节。肯定是朝着正确的方向走

然后,我将相同的
DataFrame
编码为
RDD[Array[Byte]
,其中每个
Array[Byte]
都是二进制编码的
Avro
记录,模式与底层的
DataFrame
相同。那么我会:

SizeEstimator.estimate(encodedRdd.take(1))
// res23: Long = 72
72字节——甚至更好!对于100行,它是5216字节——大约52字节一行,并且它一直在下降(对于1000条记录,它是48656字节)

因此,最好的情况是,
对象每行重850k,而相同数据的二进制Avro记录约为50字节


发生了什么事???

实际上
行本身并没有那么大。这就是为什么当您获取更多行时,您看不到as大小的显著变化。问题似乎是架构信息:

  • 当您收集数据时,实际上会得到
    GenericRowWithSchema

    val df=Seq((1,“foo”),(2,“bar”)。toDF
    df.first.getClass
    
    //res12:课堂[u但是为什么线性增加超过100行?更不用说,对于一个模式来说,81MB的内存太多了。
    Avro
    模式大小是3964字节——即使我们将模式包含在每个二进制Avro记录中,每个记录也只有4k,而不是800k!@DavidGriffin我还不确定。我甚至不知道这是否是
    StructField本身或
    SizeEstimator
    好的,我相信您已经了解了它与
    DataFrame.schema
    的关系,正如
    SizeEstimator
    所说,我的
    DataFrame.schema
    大小是8505504字节——比单个
    对象小300字节。就个人而言,我不会对fir中的行进行操作这里没有非常优雅的结构。但除此之外,我实在受不了。我必须深入挖掘,但拖动所有这些看起来不是一个好主意。这不是SizeEstimator的用途。它计算可传递对象图中的字节数,这与Row对象本身在内存或内存中的存储量无关在磁盘上。您可能正在计算分区簿记等中的巨大缓存数据结构。@SeanOwen这里发生了一些奇怪的事情,似乎都是关于
    StructField
    (没有上下文,没有
    DataFrames
    ,单个
    StructField
    ),它仍然给出了一个荒谬的数字(271872172)。我认为这种方法永远不会给出您期望的数字。它不是试图告诉您行对象和它的数据字段所消耗的内存量,而是告诉您它的整个可传递对象图有多大,几乎所有这些都不是行数据。