Apache XTable 0.4 常用 CLI 命令与工具详解

Apache XTable 0.4 的 XTableSparkSync 命令行全参数详解(--help 真实输出)、单表/多表 YAML/PARQUET 源实测示例、作业内 API 与常见报错排查速查

Apache XTableXTable数据湖Spark2026/10/15

Apache XTable 0.4 常用 CLI 命令与工具详解

目标:系统整理 Apache XTable 0.4.0-incubating 的命令行入口 XTableSparkSync(spark-submit 方式)的全部参数、三种运行模式(单表 / YAML 多表 / 作业内 API)的实测示例,以及常见报错的排查速查。本文所有命令与回显均在 Ubuntu 22.04 + OpenJDK 17.0.20.1 + Spark 3.4.4 环境实测取得(安装过程见《Ubuntu 22.04 安装 Apache XTable 0.4 指南》)。

一、环境信息

  • 操作系统:Ubuntu 22.04(x86_64)
  • Java:OpenJDK 17.0.20.1
  • Spark:3.4.4(Scala 2.12.17,SPARK_HOME=/opt/spark)
  • XTable:xtable-spark-runtime_2.12-0.4.0-incubating.jar(4462701 字节,存放于 /opt/xtable/)
  • 引擎依赖版本(与 XTable 0.4.0 构建矩阵一致,经 --packages 引入):Hudi 0.14.0、Iceberg 1.9.2、Delta 2.4.0
  • 测试表:Hudi 分区表 people(/opt/data/hudi-dataset/people)、Hudi 非分区表 orders、Iceberg 表 db.people、Parquet 表 items2

二、CLI 入口与全参数(--help 实测输出)

XTable 0.4.0 的命令行入口是 spark-runtime jar 内的 org.apache.xtable.spark.XTableSparkSync 类(jar tf 实测确认该类与 XTableSyncService、TableSyncSpec 同在 jar 内,org/apache/xtable 下共 2657 个类文件)。查看用法:

$SPARK_HOME/bin/spark-submit \
  --class org.apache.xtable.spark.XTableSparkSync \
  xtable-spark-runtime_2.12-0.4.0-incubating.jar \
  --help

实测完整回显(逐行照抄):

usage: xtable-spark-sync [--basepath <arg>] [--datapath <arg>]
       [--datasetconfig <path>] [--help] [--namespace <arg>]
       [--partitionspec <arg>] [--sourceformat <arg>] [--tablename <arg>]
       [--targets <arg>] [--usedeltakernel]
    --basepath <arg>         The base path of the source table
                             (single-table mode)
    --datapath <arg>         The path of the data files if different from
                             the base path
    --datasetconfig <path>   Path to a YAML dataset config (sourceFormat,
                             targetFormats, datasets[]) to sync multiple
                             tables in one run; may be a local or cloud
                             (s3/gcs/abfs) path. Mutually exclusive with
                             --basepath/--sourceformat/--targets.
    --help                   Displays help information
    --namespace <arg>        The dot-separated table namespace
    --partitionspec <arg>    The Hudi source partition field spec, e.g.
                             level:VALUE
    --sourceformat <arg>     The source table format, e.g. HUDI, DELTA or
                             ICEBERG (single-table mode)
    --tablename <arg>        The table name; defaults to the last segment
                             of the base path
    --targets <arg>          Comma-separated target formats to sync to,
                             e.g. ICEBERG,DELTA (single-table mode)
    --usedeltakernel         Use the Spark-free Delta Kernel
                             implementation for the Delta source and/or
                             target instead of the default delta-core
                             path. When omitted this is auto-enabled on
                             Spark 3.5+ (where the bundled delta-core does
                             not run); pass it explicitly to force Kernel
                             on any Spark version.

参数要点:

参数说明默认值 / 约束
--basepath源表基础路径(单表模式)与 --datasetconfig 互斥
--sourceformat源格式:HUDI、ICEBERG、DELTA、PAIMON、PARQUET(后两个只读源)单表模式必填
--targets目标格式,逗号分隔,如 ICEBERG,DELTA单表模式必填
--datapath数据文件所在路径(与 basepath 不同时指定)实测结论:目标格式的元数据也写在 datapath 下,而非 basepath
--tablename表名默认取 basepath 最后一段
--namespace点分命名空间可选
--partitionspecHudi 源分区字段声明,如 city:VALUE仅 Hudi 源需要
--datasetconfigYAML 多表配置文件路径与 --basepath/--sourceformat/--targets 互斥
--usedeltakernel强制 Delta 走 Delta Kernel 路径省略时 Spark 3.5+ 自动启用;Spark 3.4 默认走 delta-core

源/目标格式支持矩阵(--targets 可选值由源决定,Paimon、Parquet 不可作目标):

源 ↓ / 目标 →HUDIICEBERGDELTA
HUDI–✅✅
ICEBERG✅–✅
DELTA✅✅–
PAIMON(只读源)✅✅✅
PARQUET(只读源)✅✅✅

三、单表模式:Hudi 源转 Iceberg 与 Delta(实测)

$SPARK_HOME/bin/spark-submit \
  --class org.apache.xtable.spark.XTableSparkSync \
  --master 'local[1]' \
  --packages org.apache.hudi:hudi-spark3.4-bundle_2.12:0.14.0,org.apache.iceberg:iceberg-core:1.9.2,io.delta:delta-core_2.12:2.4.0 \
  --conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
  xtable-spark-runtime_2.12-0.4.0-incubating.jar \
  --basepath /opt/data/hudi-dataset/people \
  --sourceformat HUDI \
  --targets ICEBERG,DELTA \
  --partitionspec city:VALUE

关键回显(节选,完整日志约 10 万字符):

INFO XTableSparkSync: Starting XTable sync for people (/opt/data/hudi-dataset/people)
INFO XTableSyncService: Running XTable sync for table people (HUDI -> [ICEBERG, DELTA]) at /opt/data/hudi-dataset/people
INFO HoodieTableMetaClient: Loading HoodieTableMetaClient from file:///opt/data/hudi-dataset/people
INFO HoodieTableConfig: Loading table properties from file:/opt/data/hudi-dataset/people/.hoodie/hoodie.properties
INFO ConversionController: Sync is successful for the following formats ICEBERG,DELTA
INFO XTableSparkSync: Completed XTable sync for people

同步后同一目录内三格式元数据并存(实测 ls):

_delta_log
city=DFW
city=LA
city=NYC
city=ORD
city=SEA
city=SFO
metadata

说明:city=* 为 Hudi 数据分区目录(原始数据文件,未动);metadata/ 是 Iceberg 元数据(清单 avro + v*.metadata.json);_delta_log/ 是 Delta 事务日志;.hoodie/ 为源 Hudi 元数据(ls 默认不显示,ls -a 可见)。

四、--datapath:Iceberg 源转 Hudi 与 Delta(实测)

Iceberg 表数据文件在 <basepath>/data 子目录下时,必须传 --datapath:

$SPARK_HOME/bin/spark-submit \
  --class org.apache.xtable.spark.XTableSparkSync \
  --master 'local[1]' \
  --packages org.apache.hudi:hudi-spark3.4-bundle_2.12:0.14.0,org.apache.hudi:hudi-java-client:0.14.0,org.apache.iceberg:iceberg-core:1.9.2,io.delta:delta-core_2.12:2.4.0 \
  --conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
  xtable-spark-runtime_2.12-0.4.0-incubating.jar \
  --basepath /opt/data/iceberg-dataset/db/people \
  --datapath /opt/data/iceberg-dataset/db/people/data \
  --sourceformat ICEBERG \
  --targets HUDI,DELTA

关键回显:

INFO XTableSyncService: Running XTable sync for table people (ICEBERG -> [HUDI, DELTA]) at /opt/data/iceberg-dataset/db/people
INFO ConversionController: Sync is successful for the following formats HUDI,DELTA

实测两点重要结论:

  1. HUDI 作目标必须引入 hudi-java-client(上面的 --packages 已含)。缺失时 HUDI 目标会被服务加载器跳过,日志出现:
    WARN ConversionTargetFactory: Skipping a registered ConversionTarget whose engine library is not on the classpath (java.util.ServiceConfigurationError: org.apache.xtable.spi.sync.ConversionTarget: org.apache.xtable.hudi.HudiConversionTarget Unable to get public no-arg constructor)
    
  2. 指定 --datapath 时,目标格式元数据写在 datapath 下。实测目录(ls -a):
    /opt/data/iceberg-dataset/db/people/:
    data
    metadata
    
    /opt/data/iceberg-dataset/db/people/data/:
    .hoodie
    _delta_log
    city=DFW
    city=NYC
    city=ORD
    city=SEA
    city=SFO
    
    即 Hudi 目标的 .hoodie/ 与 Delta 目标的 _delta_log/ 都生成在数据文件目录内,读取目标表时也用该路径。

五、多表模式:--datasetconfig(实测)

--datasetconfig 指向 YAML 配置,一次 spark-submit 同步多张同源格式表。实测配置文件 /opt/xtable/my_config.yaml:

sourceFormat: HUDI
targetFormats:
  - ICEBERG
  - DELTA
datasets:
  -
    tableBasePath: file:///opt/data/hudi-dataset/people
    tableName: people
    partitionSpec: city:VALUE
  -
    tableBasePath: file:///opt/data/hudi-dataset/orders
    tableName: orders

YAML 字段说明:

字段说明
sourceFormat源格式(整份配置共用):HUDI / ICEBERG / DELTA / PAIMON / PARQUET
targetFormats目标格式列表
datasets[].tableBasePath表基础路径,本地路径用 file:/// 前缀,云存储用 s3://、gs://、abfs://
datasets[].tableDataPath数据文件路径,与 basepath 不同时填写(如 Iceberg 的 <base>/data)
datasets[].tableName表名
datasets[].partitionSpecHudi 源分区声明,如 city:VALUE;多级分区逗号分隔,如 time_millis:DAY:yyyy-MM-dd,type:VALUE
datasets[].namespace点分命名空间

执行命令:

$SPARK_HOME/bin/spark-submit \
  --class org.apache.xtable.spark.XTableSparkSync \
  --master 'local[1]' \
  --packages org.apache.hudi:hudi-spark3.4-bundle_2.12:0.14.0,org.apache.iceberg:iceberg-core:1.9.2,io.delta:delta-core_2.12:2.4.0 \
  --conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
  xtable-spark-runtime_2.12-0.4.0-incubating.jar \
  --datasetconfig /opt/xtable/my_config.yaml

关键回显(两张表依次同步,一次成功):

INFO XTableSyncService: Running XTable sync for table people (HUDI -> [ICEBERG, DELTA]) at file:///opt/data/hudi-dataset/people
INFO XTableSyncService: Running XTable sync for table orders (HUDI -> [ICEBERG, DELTA]) at file:///opt/data/hudi-dataset/orders
INFO ConversionController: Sync is successful for the following formats ICEBERG,DELTA

六、PARQUET 只读源(实测)

PARQUET 源直接把一个 Parquet 目录提升为三格式可读的表:

$SPARK_HOME/bin/spark-submit \
  --class org.apache.xtable.spark.XTableSparkSync \
  --master 'local[1]' \
  --packages org.apache.hudi:hudi-spark3.4-bundle_2.12:0.14.0,org.apache.hudi:hudi-java-client:0.14.0,org.apache.iceberg:iceberg-core:1.9.2,io.delta:delta-core_2.12:2.4.0 \
  --conf spark.serializer=org.apache.spark.serializer.KryoSerializer \
  xtable-spark-runtime_2.12-0.4.0-incubating.jar \
  --basepath /opt/data/parquet-dataset/items2 \
  --sourceformat PARQUET \
  --targets ICEBERG,DELTA,HUDI

关键回显:

INFO ConversionController: Sync is successful for the following formats HUDI,ICEBERG,DELTA

实测注意事项:

  1. 源 Parquet 文件建议用非空(required)字段 schema 写出。实测用 Spark 全 nullable schema 写出的 Parquet,同步时报:
    org.apache.hudi.exception.HoodieException: Failed to generate column stats records for metadata table
    Caused by: org.apache.avro.AvroRuntimeException: Not a record: ["null",{"type":"record","name":"spark_schema",...
    
    将字段定义为非空后同步成功。
  2. PARQUET 源同步出的 Delta/Iceberg 目标,文件级行数统计(numRecords)为 0(实测 Delta 事务日志中 add 动作带 numRecords:0 但 minValues/maxValues 正确;Hudi 目标不受影响)。读取端 count() 在 Delta/Iceberg 端返回 0。属上游解析器未填充记录数的已知行为,生产使用 PARQUET 源需自行评估。

七、作业内 API(XTableSyncService)

xtable-spark-runtime 同时提供嵌入 API,在既有 Spark 作业写完表后顺手同步(官方文档示例,0.4.0 jar 内实测存在对应类)。Java 写法:

import java.util.Arrays;
import org.apache.xtable.spark.TableSyncSpec;
import org.apache.xtable.spark.XTableSyncService;

String basePath = "s3://example-warehouse/db/orders";

// 作业已有的写入
df.write().format("hudi").options(hudiOptions).mode("append").save(basePath);

// 追加的一行同步调用
new XTableSyncService()
    .sync(
        TableSyncSpec.builder()
            .key("orders")
            .basePath(basePath)
            .sourceFormat("HUDI")
            .targets(Arrays.asList("ICEBERG", "DELTA"))
            .build(),
        spark.sparkContext().hadoopConfiguration());

要点:TableSyncSpec 的 useDeltaKernel(true) 等价于 CLI 的 --usedeltakernel;同步增量运行并在目标元数据里记录水位,可安全地在每次写入后调用。以 provided 依赖引入同一坐标 org.apache.xtable:xtable-spark-runtime_2.12:0.4.0-incubating 即可编译。

八、REST 服务形态

源码 xtable-service 模块(Quarkus)提供 HTTP 转换服务(ConversionResource / ConversionService,实测源码目录存在)。通过源码构建得到(构建方法见《Ubuntu 22.04 安装 Apache XTable 0.4 指南》方案二),适合平台化场景把转换能力作为常驻服务对外提供;常规批场景优先用第二至六节的 spark-submit 方式。

九、常见报错排查速查(全部实测)

报错(关键字)根因解决
NoSuchMethodError: HoodieTableMetaClient$Builder.setConf--packages 的 Hudi 版本过高(如 1.2.0),与 0.4.0 构建矩阵不符Hudi 用 0.14.0:org.apache.hudi:hudi-spark3.4-bundle_2.12:0.14.0
java.lang.IllegalArgumentException at TimelineLayoutVersion.<init>源表由 Hudi 1.x 写出(timeline layout v2),0.4.0 的 Hudi 0.14 读不了用 Hudi 0.14.0 重写源表,或升级 XTable 至支持 Hudi 1.x 的版本
NoSuchMethodError: LogicalTypes$TimestampNanosIceberg 1.9.2 需要 Avro 1.12.0,Spark 自带的 avro-1.11.1.jar 优先级更高用 Avro 1.12.0 替换 $SPARK_HOME/jars/avro-1.11.1.jar(替换前备份)
ClassCastException: PartitionData cannot be cast to IndexedRecordICEBERG 源时用了 iceberg-spark-runtime 包,其内置类与主类加载器冲突ICEBERG 源/目标场景改用 org.apache.iceberg:iceberg-core:1.9.2
Skipping a registered ConversionTarget(且目标列表缺 HUDI)缺 hudi-java-client--packages 追加 org.apache.hudi:hudi-java-client:0.14.0
IllegalStateException: 1 of 1 table sync(s) failed; see logs above上游任一目标同步失败在其上方日志找 ERROR XTableSparkSync: XTable sync failed for <表名> 及堆栈
Failed to generate column stats records for metadata table + Not a recordPARQUET 源文件 schema 全 nullable,footer 为 union 包裹用非空字段 schema 重写 Parquet 文件

十、参考链接