当spark-csv无法将DataFrame保存到文件时,有没有解释?

时间:2019-10-16 05:45:07

标签: apache-spark spark-csv

dataFrame.coalesce(1).write().save("path")有时仅写入_SUCCESS和._SUCCESS.crc文件,即使在非空输入DataFrame上也没有预期的* .csv.gz

文件保存代码:

private static void writeCsvToDirectory(Dataset<Row> dataFrame, Path directory) {
    dataFrame.coalesce(1)
            .write()
            .format("csv")
            .option("header", "true")
            .option("delimiter", "\t")
            .option("codec", "org.apache.hadoop.io.compress.GzipCodec")
            .mode(SaveMode.Overwrite)
            .save("file:///" + directory);
}

文件获取代码:

static Path getTemporaryCsvFile(Path directory) throws IOException {
    String glob = "*.csv.gz";
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, glob)) {
        return stream.iterator().next();
    } catch (NoSuchElementException e) {
        throw new RuntimeException(getNoSuchElementExceptionMessage(directory, glob), e);
    }
}

文件获取错误示例:

java.lang.RuntimeException: directory /tmp/temp5889805853850415940 does not contain a file with glob *.csv.gz. Directory listing:
    /tmp/temp5889805853850415940/_SUCCESS, 
    /tmp/temp5889805853850415940/._SUCCESS.crc

我依靠这种期望,有人可以向我解释为什么会这样吗?

3 个答案:

答案 0 :(得分:2)

  

输出文件(必须按逻辑)至少应包含标题行和一些数据行。但是他根本不存在

此评论有点误导。根据Github上的代码,只有在Dataframe为空并且不会产生SUCCESS文件时,才会发生这种情况。考虑到这些文件存在-数据框不为空,并且触发了代码中的writeCsvToDirectory

我有几个问题:

  • 您的Spark作业是否顺利完成?
  • SUCCESS文件的时间戳是否已更新?

我的两个主要嫌疑人是:

  1. coalesce(1)-如果您有很多数据,则可能会失败
  2. SaveMode.Overwrite-我觉得这些成功文件位于以前运行的那个文件夹中

答案 1 :(得分:1)

选择写入csv文件取决于您的存储。 如果您在hdfs上写就可以了。但是,无论何时决定写入本地文件系统,都必须注意不要在驱动程序本地文件系统中写入任何内容,并且数据将位于worker的文件系统中,并且应该在worker的存储中找到它们。< / p>

两个解决方案:

  1. 以本地模式运行Spark

设置本机本地[NUMBER_OF_CORES],您可以通过--master local[10]配置

提交作业
  1. 写入分布式文件系统

将数据写入s3,hdfs等分布式文件系统中...

答案 2 :(得分:0)

我自己的解决方案解决了这个问题。

我将.save("file://"替换为hadoopFileSystem.copyToLocalFile

问题是.save("file://仅适用于SparkSession.builder().master("local"),其中hdfs://是由上级file://模拟的。

我在理论上可能是错的,但确实有效。

static Path writeCsvToTemporaryDirectory(Dataset<Row> dataFrame) throws IOException {
    String temporaryDirectoryName = getTemporaryDirectoryName();

    writeCsvToDirectory(dataFrame, temporaryDirectoryName, sparkContext);

    return Paths.get(temporaryDirectoryName);
}

static void writeCsvToDirectory(Dataset<Row> dataFrame, String directory) throws IOException {
    dataFrame.coalesce(1)
        .write()
        .option("header", "true")
        .option("delimiter", "\t")
        .option("codec", "org.apache.hadoop.io.compress.GzipCodec")
        .mode(SaveMode.Overwrite)
        .csv(directory);

    FileSystem hadoopFileSystem = FileSystem.get(sparkContext.hadoopConfiguration());

    hadoopFileSystem.copyToLocalFile(true,
        new org.apache.hadoop.fs.Path(directory),
        new org.apache.hadoop.fs.Path(directory));
}

static Path getTemporaryCsvFile(Path directory) throws IOException {
    String glob = "*.csv.gz";

    try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, glob)) {
        return stream.iterator().next();
    } catch (NoSuchElementException e) {
        throw new RuntimeException(getNoSuchElementExceptionMessage(directory, glob), e);
    }
}

Path temporaryDirectory = writeCsvToTemporaryDirectory(dataFrame);
Path temporaryFile = DataFrameIOUtils.getTemporaryCsvFile(temporaryDirectory);

try {
    return otherStorage.upload(temporaryFile, name, fields).join();
} catch (InterruptedException | ExecutionException e) {
    throw new RuntimeException(e);
} finally {
    removeTemporaryDirectory(temporaryDirectory);
}