在o.a.spark.sql.sources.v2.writer.DataWriter.writeRecord实现中出现重复的记录吗?

时间:2019-02-23 09:28:59

标签: apache-spark apache-spark-sql

我们目前正在探索Apache Spark(使用Hadoop)以执行大规模 数据转换(使用Java)。

我们正在使用新的(和实验性的)DataSourceV2接口来构建我们的自定义 输出数据文件。其中的一个部分是org.apache.spark.sql.sources.v2.writer.DataWriter的实现 接口。除了一个问题外,一切都运转良好:

org.apache.spark.sql.sources.v2.writer.DataWriter.write(record)方法通常(但不总是) 为相同的输入记录调用了两次。

我希望这里有足够的代码让您了解我们正在做的事情:

基本上,我们有许多通过Spark应用程序登陆的输入数据集 使用类似于以下代码的代码将其放入Hadoop表:

  final Dataset<Row> jdbcTableDataset = sparkSession.read()
    .format("jdbc")
    .option("url", sqlServerUrl)
    .option("dbtable", tableName)
    .option("user", jdbcUser)
    .option("password", jdbcPassword)
    .load();

  final DataFrameWriter<Row> dataFrameWriter = jdbcTableDataset.write();
  dataFrameWriter.save(hdfsDestination + "/" + tableName);

这些表的价值大概有五十个。我知道没有重复 在数据中,因为dataFrameWriter.count()dataFrameWriter.distinct().count() 返回相同的值。

转换过程包括对这些表执行联接操作并编写 结果以自定义格式保存到(共享)文件系统中的文件中。结果行包含唯一键,  dataGroup列,dataSubGroup列和其他40列。选择的记录是  由dataGroupdataSubGroup和键排序。

每个输出文件都由dataGroup列来区分,该列用于对write操作进行分区:

  final Dataset<Row> selectedData = dataSelector.selectData();
  selectedData
    .write()
    .partitionBy("dataGroup")
    .format("au.com.mycompany.myformat.DefaultSource")
    .save("/path/to/shared/directory/");

为使您对规模有所了解,最终选择的数据包括五千六百万 记录,在大约3000 dataGroup个文件之间分配不均。大,但不大。

partitionBy("dataGroup")可以确保每个dataGroup文件都由一个 单执行人。到目前为止一切顺利。

我的数据源实现了新的(和实验性的)DataSourceV2接口:

package au.com.mycompany.myformat;

import java.io.Serializable;
import java.util.Optional;

import org.apache.spark.sql.SaveMode;
import org.apache.spark.sql.sources.DataSourceRegister;
import org.apache.spark.sql.sources.v2.DataSourceOptions;
import org.apache.spark.sql.sources.v2.WriteSupport;
import org.apache.spark.sql.sources.v2.writer.DataSourceWriter;
import org.apache.spark.sql.types.StructType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DefaultSource implements DataSourceRegister, WriteSupport , Serializable {

    private static final Logger logger = LoggerFactory.getLogger(DefaultSource.class);

    public DefaultSource() {
        logger.info("created");
    }

    @Override
    public String shortName() {
        logger.info("shortName");
        return "myformat";
    }

    @Override
    public Optional<DataSourceWriter> createWriter(String writeUUID, StructType schema, SaveMode mode, DataSourceOptions options) {
        return Optional.of(new MyFormatSourceWriter(writeUUID, schema, mode, options));
    }

}

有一个DataSourceWriter实现:

public class MyFormatSourceWriter implements DataSourceWriter, Serializable {

  ...

}

DataSourceWriterFactory实现:

public class MyDataWriterFactory implements DataWriterFactory<InternalRow> {

  ...

}

,最后是DataWriter实现。似乎创建了DataWriter并将其发送到 每个执行者。因此,每个DataWriter将处理许多数据组。

每个记录都有一个唯一的键列。

public class MyDataWriter implements DataWriter<InternalRow>, Serializable {

  private static final Logger logger = LoggerFactory.getLogger(XcdDataWriter.class);

  ...

  MyDataWriter(File buildDirectory, StructType schema, int partitionId) {
      this.buildDirectory = buildDirectory;
      this.schema = schema;
      this.partitionId = partitionId;
      logger.debug("Created MyDataWriter for partition {}", partitionId);
  }

  private String getFieldByName(InternalRow row, String fieldName) {
      return Optional.ofNullable(row.getUTF8String(schema.fieldIndex(fieldName)))
        .orElse(UTF8String.EMPTY_UTF8)
        .toString();
  }

  /**
   * Rows are written here. Each row has a unique key column as well as a dataGroup
   * column. Right now we are frequently getting called with the same record twice.
   */ 
  @Override
  public void write(InternalRow record) throws IOException {
      String nextDataFileName = getFieldByName(record, "dataGroup") + ".myExt";

      // some non-trivial logic for determining the right output file
      ...

      // write the output record
        outputWriter.append(getFieldByName(row, "key")).append(',')
          .append(getFieldByName(row, "prodDate")).append(',')
          .append(getFieldByName(row, "nation")).append(',')
          .append(getFieldByName(row, "plant")).append(',')
        ...              

  }

  @Override
  public WriterCommitMessage commit() throws IOException {
      ...
      outputWriter.close();
      ...
      logger.debug("Committed partition {} with {} data files for zip file {} for a total of {} zip files",
        partitionId, dataFileCount, dataFileName, dataFileCount);
      return new MyWriterCommitMessage(partitionId, dataFileCount);
  }

  @Override
  public void abort() throws IOException {
      logger.error("Failed to collect data for schema: {}", schema);
      ...
  }

}

现在,我正在通过跟踪最后处理的密钥并忽略该密钥来解决此问题 重复。

0 个答案:

没有答案