Spark分区表queryExecution.analyzed.stats.sizeInBytes始终返回最大值的问题
问题解答
这不是Spark的Bug,是分区表的统计信息机制导致的,需要采用不同方法来获取准确大小,具体原因和解决方式如下:
原因分析
Spark的stats.sizeInBytes字段,对于未收集过统计信息的分区表,会默认返回BigInt的最大值(9223372036854775807),用来表示未知的表大小。这是因为分区表的元数据默认不会自动维护全表的总大小统计,只有非分区表会在元数据中存储基础大小信息,或者分区表手动收集统计信息后才会更新这个值。
解决方法
方法1:收集表统计信息后再获取
先执行ANALYZE TABLE命令收集全表统计信息,之后再用你原来的代码就能拿到准确的sizeInBytes:
// 先执行统计收集 spark.sql(s"ANALYZE TABLE $db.$table_name COMPUTE STATISTICS") // 再获取大小 val table_size_bytes = spark.read.table(s"$db.$table_name").queryExecution.analyzed.stats.sizeInBytes
- 注意:如果表数据频繁更新,需要定期重新执行
ANALYZE TABLE来同步统计信息;如果还需要列级统计,可以用ANALYZE TABLE $db.$table_name COMPUTE STATISTICS FOR ALL COLUMNS。
方法2:从元数据存储查询分区大小总和
如果不想收集统计信息,可以直接查询元数据中每个分区的大小并求和。以Hive Metastore为例,用Spark SQL执行查询:
SELECT SUM(CAST(pp.PARAM_VALUE AS BIGINT)) AS total_table_size FROM hive_metastore.DBS d JOIN hive_metastore.TABLES t ON d.DB_ID = t.DB_ID JOIN hive_metastore.PARTITIONS p ON t.TBL_ID = p.TBL_ID JOIN hive_metastore.PARTITION_PARAMS pp ON p.PART_ID = pp.PART_ID WHERE d.NAME = 'database' AND t.TBL_NAME = 'table_name' AND pp.PARAM_KEY = 'totalSize'
也可以用Scala代码封装这个查询逻辑,直接在程序中计算总和。
方法3:通过文件系统API计算实际存储大小
如果表存储在HDFS、S3等文件系统上,可以直接遍历表的存储路径,计算所有文件的总大小:
import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.spark.sql.catalyst.TableIdentifier // 获取表的存储路径 val tableId = TableIdentifier(table_name, Some(db)) val tableMetadata = spark.sessionState.catalog.getTableMetadata(tableId) val tablePath = tableMetadata.location // 计算总大小 val fs = FileSystem.get(spark.sparkContext.hadoopConfiguration) val contentSummary = fs.getContentSummary(new Path(tablePath)) val tableSizeBytes = contentSummary.getLength
这种方法直接读取文件系统的实际数据大小,结果最准确,但如果表包含大量小文件,遍历路径的性能会有所下降。
内容的提问来源于stack exchange,提问作者t3b4n
相关产品推荐
相关产品推荐

