我的Spark结构化流作业连续生成实木复合地板文件,我想在过期后将其删除(比方说30天后)。
我将我的实木复合地板数据存储为RFC3339 / ISO8601中以事件日期为分区密钥的分区数据,以便在基于cron作业的HDFS级别上相当轻松地进行内务处理(删除所有使用partitionkey 但是,自从我介绍了Spark Streaming之后,Spark将元数据写入要写入数据本身旁边的名为 对此的简单解决方案是仅禁用 我认为,然后我可以只使用spark进行删除,这样它就可以删除实木复合地板hdfs文件,并且更新元数据。但是,只需执行 不起作用。 有什么解决方法可以删除旧数据,但_spark_metadata
的文件夹中。如果我现在仅删除过期的HDFS文件并在整个数据集上运行spark batch-job,由于找不到文件,该作业将失败。批处理作业将读取元数据,并期望已删除的文件存在。 _spark_metadata
目录的创建,如此处所述:disabling _spark_metadata in Structured streaming in spark 2.3.0。但是由于我不想在定期进行批处理分析时失去读取数据的性能,因此我想知道是否有更好的解决方案。 session.sql(String.format("DELETE FROM parquet.`%s` WHERE partitionKey < " + oldestAllowedPartitionAge, path.toString()));
DELETE
可惜是Spark中不受支持的操作... _spark_metadata
文件夹仍在工作吗?
答案 0 :(得分:2)
据我了解,_spark_metadata
的主要目的是确保容错并避免列出所有要处理的文件:
为了在维护的同时正确处理部分故障 语义一次,每批文件被写到 唯一目录,然后原子地附加到元数据日志。什么时候 基于
DataSource
的实木复合地板被初始化以进行读取,我们首先 检查此日志目录并在以下情况下使用它而不是文件列表 礼物。
https://github.com/apache/spark/commit/6bc4be64f86afcb38e4444c80c9400b7b6b745de
您引用的链接(disabling _spark_metadata in Structured streaming in spark 2.3.0)解释说,问题出自检查点状态不一致-检查点生成了元数据,但后来用户手动将其删除,并且当他重新启动查询时,由于检查点期望包含元数据而失败文件。
要查看缺少元数据是否会使您的批处理失败,请查看org.apache.spark.sql.execution.datasources.DataSource#resolveRelation方法,在其中您可以找到两种情况下的模式匹配:
// We are reading from the results of a streaming query. Load files from the metadata log
// instead of listing them using HDFS APIs.
case (format: FileFormat, _)
if FileStreamSink.hasMetadata(
caseInsensitiveOptions.get("path").toSeq ++ paths,
sparkSession.sessionState.newHadoopConf()) =>
case (format: FileFormat, _) =>
val globbedPaths =
checkAndGlobPathIfNecessary(checkEmptyGlobPath = true, checkFilesExist = checkFilesExist)
hasMetadata
方法如下:
def hasMetadata(path: Seq[String], hadoopConf: Configuration): Boolean = {
path match {
case Seq(singlePath) =>
try {
val hdfsPath = new Path(singlePath)
val fs = hdfsPath.getFileSystem(hadoopConf)
if (fs.isDirectory(hdfsPath)) {
fs.exists(new Path(hdfsPath, metadataDir))
} else {
false
}
} catch {
case NonFatal(e) =>
logWarning(s"Error while looking for metadata directory.")
false
}
case _ => false
}
}
如您所见,没有失败的风险(至少通过阅读代码!)。如果有的话,请提供更多背景信息,因为问题可能出在其他地方。
关于性能方面的问题,此_spark_metadata
仅包含文件列表,因此,Spark当然首先需要从您的输入目录中列出文件。但是根据我的经验,这不是最昂贵的操作。例如,在AWS S3上列出包含1297个文件的目录大约需要9秒钟。之后,由您决定是要进行简单的清洁过程还是要稍微慢一些的批处理过程。如果您有更多类似的文件,也许您还应该将它们分组为更大的文件,例如256 MB或更多?
不过,如果您想保留_spark_metadata
,也许可以通过清洁应用程序删除文件。但是这将具有挑战性,因为您将有2个应用程序(流和清理)在同一个数据上运行。
您可以在此处找到有关_spark_metadata
的更多信息:How to change the location of _spark_metadata directory?
答案 1 :(得分:0)
这实际上是结构化流(SPARK-24295)中的已知问题之一,尽管它仅在海量输入文件中发生,并且最终用户正在采取自己的解决方法。例如,停止查询->删除旧的输入文件->手动处理元数据以清除它们->重新启动查询。
鉴于手动操作元数据并非微不足道且不理想(假设它应停止流查询,并迫使最终用户了解元数据的格式),因此建议使用SPARK-27188作为替代方案-应用保留并清除过时的数据从元数据输入文件。
答案 2 :(得分:0)
据我所知,有三种方法可以解决此问题:
1)使用spark.load(filePathsUsingGlobRegex)
仅加载需要读取的文件,这样spark不需要加载所有文件,因此不需要spark_metadata。
优点:您仍然可以获得spark_metadata的好处(读取速度更快,仍然可以确保一次语义准确)
缺点:您必须自己构建文件的路径,如果您将数据存储在各种分区策略中,则可能会更麻烦。
2)不要在输出目录disabling _spark_metadata in Structured streaming in spark 2.3.0
中创建spark_metadata优点:清理很简单
缺点:您失去了spark_metadata的好处。
3)了解并更新spark_metadata文件,在升级时删除较旧的文件。
优点:您同时拥有保留工作和spark_metadata的好处。
缺点:您必须手动更改_spark_metadata,这可能很难维护。鉴于这是内部的火花,并且可以改变。