我有成千上万个较小的镶木地板文件,我希望定期通过Spark阅读。我的应用程序运行,但是在使用执行程序节点读取文件之前,驱动程序节点似乎正在获取每个文件的状态。我读了一点,这是推断架构和分区所必需的。我尝试这样提供它们:
sparkSession.baseRelationToDataFrame(
DataSource
.apply(
sparkSession,
paths = paths, // List of thousands of parquet files in S3
partitionColumns = Seq("my_join_column"),
userSpecifiedSchema = Some(schema),
className = "parquet",
options = Seq().toMap
)
.resolveRelation(checkFilesExist = false)
)
但是,即使提供架构列和分区列,也需要一些时间。在仔细研究了resolveRelation
代码之后,看起来仍然需要查询每个文件的状态才能构建InMemoryFileIndex
。
有什么办法可以解决这个问题?
我正在使用spark-sql 2.3.1
。
答案 0 :(得分:6)
在当前的Spark体系结构中,没有很好的方法来避免此问题。
前一阵子,我与一些Spark提交者合作进行了LazyBaseRelation
设计,该设计可能会延迟发现文件信息,直到必须知道数据源的分区数量(而不仅仅是模式)为止。在必须执行某项操作之前,从技术上讲是没有必要的,但是我们从未完成工作。即使这样,当需要执行一个动作时,您还是会受到打击。
有四种实用的方法可以加快初始文件的发现速度:
OPTIMIZE
将Parquet小文件合并为更少,更大的文件。请注意,达美航空需要额外付费。OPTIMIZE
的等效项,重写数据的子集。是否可以轻松完成此操作取决于访问模式:您必须考虑幂等性和一致性。完成初始发现后,缓存内存文件列表是您最好的朋友。有两种方法可以做到:
通过将数据注册为外部表来使用元存储。是否可以轻松完成此操作取决于数据更新模式。如果数据是自然分区的,则可以使用DDL添加分区,并且可以轻松实现上述策略(4)。
构建自己的表管理器。这是我们所做的,因为元存储实现对模式演变有不可接受的限制。您必须决定范围:驱动程序/ JVM-和SparkSession
是两个显而易见的选择。
祝你好运!