Apache XTable 0.4 常用 CLI 命令与工具详解
Apache XTable 0.4 的 XTableSparkSync 命令行全参数详解(--help 真实输出)、单表/多表 YAML/PARQUET 源实测示例、作业内 API 与常见报错排查速查
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 | 点分命名空间 | 可选 |
--partitionspec | Hudi 源分区字段声明,如 city:VALUE | 仅 Hudi 源需要 |
--datasetconfig | YAML 多表配置文件路径 | 与 --basepath/--sourceformat/--targets 互斥 |
--usedeltakernel | 强制 Delta 走 Delta Kernel 路径 | 省略时 Spark 3.5+ 自动启用;Spark 3.4 默认走 delta-core |
源/目标格式支持矩阵(--targets 可选值由源决定,Paimon、Parquet 不可作目标):
| 源 ↓ / 目标 → | HUDI | ICEBERG | DELTA |
|---|---|---|---|
| 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
实测两点重要结论:
- 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) - 指定
--datapath时,目标格式元数据写在 datapath 下。实测目录(ls -a):即 Hudi 目标的/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.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[].partitionSpec | Hudi 源分区声明,如 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
实测注意事项:
- 源 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",... - 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$TimestampNanos | Iceberg 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 IndexedRecord | ICEBERG 源时用了 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 record | PARQUET 源文件 schema 全 nullable,footer 为 union 包裹 | 用非空字段 schema 重写 Parquet 文件 |
十、参考链接
- Spark Runtime 使用指南:https://xtable.apache.org/docs/how-to-spark-runtime
- Quick Start:https://xtable.apache.org/docs/how-to
- 功能与限制:https://xtable.apache.org/docs/features-and-limitations
- 0.4.0 发布说明:https://xtable.apache.org/releases/release-0.4.0-incubating
- Maven Central 制品:https://repo1.maven.org/maven2/org/apache/xtable/
- 源码仓库:https://github.com/apache/incubator-xtable