我很难理解以下现象:在Spark 2.2中,在Scala上,我看到用lit(null)替换文本空字符串值后,持久化的DataFrame大小发生了显著变化。
这是我用来替换空字符串值的函数:
def nullifyEmptyStrings(df:DataFrame): DataFrame = {
var in = df
for (e <- df.columns) {
in = in.withColumn(e, when(length(col(e))===0, lit(null:String)).otherwise(col(e)))
}
in
}
我观察到,在运行此函数之前,我的初始数据帧的持久化(DISK_ONLY)大小为1480MB,之后为1610MB。分区数量保持不变。
有什么想法吗?顺便说一句,无效操作很好,但我引入它的主要原因是减少洗牌大小,而且我似乎只是通过这种方式增加它。
我将自己回答这个问题,因为我们现在已经做了一些可能有用的调查。
在具有完全String列的大型(数以千万计的行)DataFrames上进行测试时,我们观察到用null替换空字符串会导致在S3上序列化为parquet时总体磁盘占用面积略有减少(1.1-1.5%)
然而,缓存MEMORY_ONLY或DISK_ONLY的数据帧分别大6%和8%。我只能推测当Column为StringType时,Spark是如何在内部表示NULL值的。。。但不管它是什么,它都比一根空绳子大。如果有什么方法可以检查这个,我会很高兴听到的。
这种现象在PySpark和Scala中是相同的。
我们使用null的目的是在复杂的联接操作中减少shuffle大小。总的来说,我们经历了相反的情况。然而,我们将继续使用null,因为isNotNull过滤器的自动下推使得在Spark SQL中编写联接更加干净。
此处的结果相同。也许还应该检查分区的数量,因为具有许多不同值的巨大分区可能将列存储为行字符串,而不是字典。