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)。我认为这种方法永远不会给出您期望的数字。它不是试图告诉您行对象和它的数据字段所消耗的内存量,而是告诉您它的整个可传递对象图有多大,几乎所有这些都不是行数据。