我正在处理一个大型数据集,该数据集由两列分隔 - plant_name
和tag_id
。第二个分区 - tag_id
有200000个唯一值,我主要通过特定tag_id
值访问数据。如果我使用以下Spark命令:
sqlContext.setConf("spark.sql.hive.metastorePartitionPruning", "true")
sqlContext.setConf("spark.sql.parquet.filterPushdown", "true")
val df = sqlContext.sql("select * from tag_data where plant_name='PLANT01' and tag_id='1000'")
我希望快速响应,因为这会解析为单个分区。在Hive和Presto中,这需要几秒钟,但在Spark中它运行了几个小时。
实际数据保存在S3存储桶中,当我提交sql查询时,Spark关闭并首先获取Hive Metastore中的所有分区(其中200000个),然后调用refresh()
强制S3对象库中所有这些文件的完整状态列表(实际调用listLeafFilesInParallel
)。
这两个操作是如此昂贵,是否有任何设置可以让Spark更早地修剪分区 - 在调用元数据存储期间,还是之后立即?
答案 0 :(得分:2)
是的,spark支持分区修剪。
Spark会执行分区目录列表(顺序或并行listLeafFilesInParallel
),以便第一次构建所有分区的缓存。同一应用程序中的查询,扫描数据利用此缓存。所以你看到的缓慢可能是因为这个缓存构建。扫描数据的后续查询使用缓存来修剪分区。
这些是显示用于填充缓存的分区的日志。
App > 16/11/14 10:45:24 main INFO ParquetRelation: Listing s3://test-bucket/test_parquet_pruning/month=2015-01 on driver
App > 16/11/14 10:45:24 main INFO ParquetRelation: Listing s3://test-bucket/test_parquet_pruning/month=2015-02 on driver
App > 16/11/14 10:45:24 main INFO ParquetRelation: Listing s3://test-bucket/test_parquet_pruning/month=2015-03 on driver
这些是显示正在修剪的日志。
App > 16/11/10 12:29:16 main INFO DataSourceStrategy: Selected 1 partitions out of 20, pruned 95.0% partitions.
在convertToParquetRelation
中提及getHiveQlPartitions
和HiveMetastoreCatalog.scala
。
答案 1 :(得分:0)
只是一个想法:
HadoopFsRelation的Spark API文档说, (https://spark.apache.org/docs/1.6.2/api/java/org/apache/spark/sql/sources/HadoopFsRelation.html)
“...从存储在文件中的Hive样式分区表中读取时 系统,它能够从路径中发现分区信息 输入目录,并在启动之前执行分区修剪 阅读数据......“
所以,我猜“listLeafFilesInParallel”不是问题。
类似的问题已经在spark jira:https://issues.apache.org/jira/browse/SPARK-10673
尽管“spark.sql.hive.verifyPartitionPath”设置为false,并且对性能没有影响,我怀疑 问题可能是由未注册的分区引起的。请列出表格的分区并验证是否全部 分区已注册。否则,请恢复您的分区,如以下链接所示:
Hive doesn't read partitioned parquet files generated by Spark
更新
我想在编写数据时设置了合适的镶木地板块大小和页面大小。
使用动态分区方法创建一个带有分区的新hive表,并将文件格式设置为镶木地板,从非分区表中加载它。 (https://cwiki.apache.org/confluence/display/Hive/DynamicPartitions) 运行一个简单的配置单元查询,然后运行一个spark程序进行比较。
免责声明:我不是火花/镶木地板专家。这个问题听起来很有意思,因此做出了回应。
答案 2 :(得分:0)
这个问题很旧,但我也想在这里发布解决方案。
spark.sql.hive.convertMetastoreParquet=false
将使用Hive镶木地板Serde代替Spark内置镶木地板Serde。 Hive的Parquet Serde不会在所有分区上执行listLeafFiles,而是仅直接从所选分区中读取。在具有许多分区和文件的表上,这要快得多(也更便宜)。随意尝试哦! :)