Apache Hudi初探(三)(与spark的结合)

简介: Apache Hudi初探(三)(与spark的结合)

背景


目前hudi的与spark的集合还是基于spark datasource V1来的,这一点可以查看hudi的source实现就可以知道:

class DefaultSource extends RelationProvider
  with SchemaRelationProvider
  with CreatableRelationProvider
  with DataSourceRegister
  with StreamSinkProvider
  with StreamSourceProvider
  with SparkAdapterSupport
  with Serializable {

闲说杂谈


继续上次的Apache Hudi初探(二)涉及的代码:

      hoodieDF.write.format("org.apache.hudi.spark3.internal")
        .option(DataSourceInternalWriterHelper.INSTANT_TIME_OPT_KEY, instantTime)
        .option(HoodieInternalConfig.BULKINSERT_INPUT_DATA_SCHEMA_DDL.key, hoodieDF.schema.toDDL)
        .options(params)
        .mode(SaveMode.Append)
        .save()

也就是说最终会调用到org.apache.hudi.spark3.internal.DefaultSource类,

public class DefaultSource extends BaseDefaultSource implements TableProvider {
  @Override
  public StructType inferSchema(CaseInsensitiveStringMap options) {
    return StructType.fromDDL(options.get(HoodieInternalConfig.BULKINSERT_INPUT_DATA_SCHEMA_DDL.key()));
  }
  @Override
  public Table getTable(StructType schema, Transform[] partitioning, Map<String, String> properties) {
    String instantTime = properties.get(DataSourceInternalWriterHelper.INSTANT_TIME_OPT_KEY);
    String path = properties.get("path");
    String tblName = properties.get(HoodieWriteConfig.TBL_NAME.key());
    boolean populateMetaFields = Boolean.parseBoolean(properties.getOrDefault(HoodieTableConfig.POPULATE_META_FIELDS.key(),
        Boolean.toString(HoodieTableConfig.POPULATE_META_FIELDS.defaultValue())));
    boolean arePartitionRecordsSorted = Boolean.parseBoolean(properties.getOrDefault(HoodieInternalConfig.BULKINSERT_ARE_PARTITIONER_RECORDS_SORTED,
        Boolean.toString(HoodieInternalConfig.DEFAULT_BULKINSERT_ARE_PARTITIONER_RECORDS_SORTED)));
    // Create a new map as the properties is an unmodifiableMap on Spark 3.2.0
    Map<String, String> newProps = new HashMap<>(properties);
    // Auto set the value of "hoodie.parquet.writelegacyformat.enabled"
    tryOverrideParquetWriteLegacyFormatProperty(newProps, schema);
    // 1st arg to createHoodieConfig is not really required to be set. but passing it anyways.
    HoodieWriteConfig config = DataSourceUtils.createHoodieConfig(newProps.get(HoodieWriteConfig.AVRO_SCHEMA_STRING.key()), path, tblName, newProps);
    return new HoodieDataSourceInternalTable(instantTime, config, schema, getSparkSession(),
        getConfiguration(), newProps, populateMetaFields, arePartitionRecordsSorted);
  }
}

可以看到该类是继承了TableProvider,也就是说是时基于DataSource V2的,而且save的方法最终会调用到getTable方法。


  • inferSchema方法的调用链如下:
    save
     ||
     \/
  saveInternal
     ||
     \/
 DataSourceV2Utils.getTableFromProvider
     ||
     \/
  provider.getTable  => provider.inferSchema(options)

"hoodie.bulkinsert.schema.ddl"会在DF.option方法中传递过来


getTable方法调用链和inferSchema一样,DataSourceUtils.createHoodieConfig已经分析过了,

最终会生成HoodieDataSourceInternalTable对象,该table对象对象的调用如下:

        val relation = DataSourceV2Relation.create(table, catalog, ident, dsOptions)
        checkPartitioningMatchesV2Table(table)
        if (mode == SaveMode.Append) {
          runCommand(df.sparkSession, "save") {
            AppendData.byName(relation, df.logicalPlan, finalOptions)
          }

其中catalog和ident为None,接下来的就是Spark的行为了,也就是说最终会生成AppendDataExec物理计划:

   case class AppendDataExec(
     table: SupportsWrite,
     writeOptions: CaseInsensitiveStringMap,
     query: SparkPlan,
     refreshCache: () => Unit) extends V2TableWriteExec with BatchWriteHelper {
   override protected def run(): Seq[InternalRow] = {
     val writtenRows = writeWithV2(newWriteBuilder().buildForBatch())
     refreshCache()
     writtenRows
   }

最终会调用HoodieDataSourceInternalBatchWrite类的方法:

 ...
 this.dataSourceInternalWriterHelper = new DataSourceInternalWriterHelper(instantTime, writeConfig, structType,
      jss, hadoopConfiguration, extraMetadata);
 ...
 @Override
 public DataWriterFactory createBatchWriterFactory(PhysicalWriteInfo info) {
   dataSourceInternalWriterHelper.createInflightCommit();
   if (WriteOperationType.BULK_INSERT == dataSourceInternalWriterHelper.getWriteOperationType()) {
     return new HoodieBulkInsertDataInternalWriterFactory(dataSourceInternalWriterHelper.getHoodieTable(),
         writeConfig, instantTime, structType, populateMetaFields, arePartitionRecordsSorted);
   } else {
     throw new IllegalArgumentException("Write Operation Type + " + dataSourceInternalWriterHelper.getWriteOperationType() + " not supported ");
   }
 }
 @Override
 public boolean useCommitCoordinator() {
   return dataSourceInternalWriterHelper.useCommitCoordinator();
 }
 @Override
 public void onDataWriterCommit(WriterCommitMessage message) {
   dataSourceInternalWriterHelper.onDataWriterCommit(message.toString());
 }
 @Override
 public void commit(WriterCommitMessage[] messages) {
   List<HoodieWriteStat> writeStatList = Arrays.stream(messages).map(m -> (HoodieWriterCommitMessage) m)
       .flatMap(m -> m.getWriteStatuses().stream().map(HoodieInternalWriteStatus::getStat)).collect(Collectors.toList());
   dataSourceInternalWriterHelper.commit(writeStatList);
 }
 @Override
 public void abort(WriterCommitMessage[] messages) {
   dataSourceInternalWriterHelper.abort();
 }

具体的这些方法的被调用过程,读者可以自行查看AppendDataExec类的Run方法,

目前这些方法的调用链如下:

 createBatchWriterFactory => dataWriter.write => dataWriter.commit/abort => dataWriter.close
     ||
     \/
 onDataWriterCommit
     ||
     \/
 commit/abort

由于这个过程涉及的东西比较多,所以下篇文章一起说明

相关文章
|
存储 分布式计算 测试技术
探索Apache Hudi核心概念 (4) - Clustering
探索Apache Hudi核心概念 (4) - Clustering
865 3
|
存储 数据采集 数据管理
一体化元数据管理平台——OpenMetadata入门宝典
一体化元数据管理平台——OpenMetadata入门宝典
4667 0
|
12月前
|
人工智能 运维 监控
Flink 智能调优:从人工运维到自动化的实践之路
本文由阿里云Flink产品专家黄睿撰写,基于平台实践经验,深入解析流计算作业资源调优难题。针对人工调优效率低、业务波动影响大等挑战,介绍Flink自动调优架构设计,涵盖监控、定时、智能三种模式,并融合混合计费实现成本优化。展望未来AI化方向,推动运维智能化升级。
1149 8
Flink 智能调优:从人工运维到自动化的实践之路
|
数据采集 SQL canal
Amoro + Flink CDC 数据融合入湖新体验
本文总结了货拉拉高级大数据开发工程师陈政羽在Flink Forward Asia 2024上的分享,聚焦Flink CDC在货拉拉的应用与优化。内容涵盖CDC应用现状、数据入湖新体验、入湖优化及未来规划。文中详细分析了CDC在多业务场景中的实践,包括数据采集平台化、稳定性建设,以及面临的文件碎片化、Schema演进等挑战。同时介绍了基于Apache Amoro的湖仓融合架构,通过自优化服务解决小文件问题,提升数据新鲜度与读写平衡。未来将深化Paimon与Amoro的结合,打造更高效的入湖生态与自动化优化方案。
870 1
Amoro + Flink CDC 数据融合入湖新体验
|
消息中间件 Kafka 数据处理
超硬核解析!Apache Hudi灵活的Payload机制
Apache Hudi 的Payload是一种可扩展的数据处理机制,通过不同的Payload我们可以实现复杂场景的定制化数据写入方式,大大增加了数据处理的灵活性。Hudi Payload在写入和读取Hudi表时对数据进行去重、过滤、合并等操作的工具类,通过使用参数 &quot;hoodie.datasource.write.payload.class&quot;指定我们需要使用的Payload class。 ​
2735 0
超硬核解析!Apache Hudi灵活的Payload机制
|
消息中间件 关系型数据库 MySQL
Apache Flink CDC 3.1.0 发布公告
Apache Flink 社区很高兴地宣布发布 Flink CDC 3.1.0!
1744 1
Apache Flink CDC 3.1.0 发布公告
|
IDE 前端开发 Java
怎样避免 Java 中的 NoSuchFieldError 异常
在Java中避免NoSuchFieldError异常的关键在于确保类路径下没有不同版本的类文件冲突,避免反射时使用不存在的字段,以及确保所有依赖库版本兼容。编译和运行时使用的类版本应保持一致。
909 8
|
关系型数据库 Linux Docker
datahub元数据管理平台从安装到使用一站式指南(未完)_datahub安装
datahub元数据管理平台从安装到使用一站式指南(未完)_datahub安装
datahub元数据管理平台从安装到使用一站式指南(未完)_datahub安装
|
Python
pandas库明明安装成功了,为什么总是导入错误?
pandas库明明安装成功了,为什么总是导入错误?
2200 4
|
分布式计算 Hadoop 网络安全
[m1pro ] ssh: connect to host localhost port 22: Connection refused
[m1pro ] ssh: connect to host localhost port 22: Connection refused
443 1