ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Apache XTable이란
    공부/데이터 2026. 4. 26. 23:44

    개요

    Apache XTable™(Incubating)은 데이터 레이크하우스의 서로 다른 오픈 테이블 포맷 간 상호운용성을 제공하는 오픈소스 메타데이터 변환 도구입니다. Apache Iceberg, Apache Hudi, Delta Lake 세 가지 주요 포맷 사이에서 데이터를 복사하거나 이동하지 않고 메타데이터만 번역하여, 하나의 물리적 데이터 셋을 여러 포맷으로 동시에 읽을 수 있게 해줍니다.

    원래 OneTable이라는 이름으로 Microsoft, Google, Onehouse가 공동으로 오픈소스화하였으며, 이후 Apache Software Foundation에 기증되어 현재 Apache XTable™ (Incubating) 으로 불리고 있습니다.

    💡 핵심 포인트: XTable은 새로운 테이블 포맷이 아닙니다. 기존 포맷 간의 메타데이터 변환기입니다.


    Apache XTable이 필요한 이유

    데이터 레이크하우스 생태계에는 세 가지 주요 오픈 테이블 포맷이 공존하고 있습니다.

    포맷 주요 강점 주요 지지 업체
    Apache Hudi 고빈도 업서트, 낮은 레이턴시 스트리밍 수집 Uber, AWS
    Apache Iceberg 대규모 분석, 강력한 스키마 진화 Netflix, Apple, Snowflake
    Delta Lake Databricks 최적화, ACID 트랜잭션 Databricks, Microsoft

    문제는 각 도구나 플랫폼이 특정 포맷만 지원하는 경우가 많다는 점입니다.

    • Databricks는 Delta Lake에 최적화되어 있습니다.
    • Snowflake는 주로 Iceberg를 지원합니다.
    • Apache Hudi로 수집하는 팀과 Iceberg로 분석하는 팀이 같은 조직 안에 공존하기도 합니다.

    이처럼 포맷 파편화(format fragmentation) 가 발생하면 동일한 데이터를 중복 저장하거나, 포맷을 전환할 때 전체 데이터를 재작성해야 하는 비용이 발생합니다. Apache XTable은 이 문제를 메타데이터 변환만으로 해결합니다.


    아키텍처 및 동작 원리

    Apache XTable는 다음 원칙으로 동작합니다.

    1. 데이터 파일은 그대로 유지됩니다. 모든 포맷은 공통적으로 Parquet 파일을 사용하며, XTable은 이 파일을 건드리지 않습니다.
    2. 메타데이터만 번역합니다. 각 포맷의 메타데이터 디렉토리(_delta_log/, metadata/, .hoodie/)에 새 메타데이터를 생성합니다.
    3. Primary(원본) + Secondary(대상) 구조입니다. 쓰기는 Primary 포맷으로만 이루어지고, XTable이 Secondary 포맷의 메타데이터를 동기화합니다.

    두 가지 동기화 모드:

    • Incremental(증분) 모드: 마지막 동기화 이후 변경된 커밋만 처리합니다. 대용량 테이블에 적합하며 성능이 우수합니다.
    • Full(전체) 모드: 테이블 전체를 처음부터 재동기화합니다.

    Apache XTable 장단점 및 한계

    장점

    • 데이터 복사 없음: 물리적 데이터 파일을 이동하거나 복사하지 않아 스토리지 비용이 증가하지 않습니다.
    • 전방향(omni-directional) 변환: Hudi ↔ Iceberg ↔ Delta Lake 간 어떤 방향으로도 변환이 가능합니다.
    • 점진적 마이그레이션 지원: 전환 기간 동안 두 포맷을 동시에 유지할 수 있어 무중단 마이그레이션이 가능합니다.
    • 카탈로그 동기화: Hive Metastore, AWS Glue와의 메타데이터 등록 자동화를 지원합니다.
    • 증분 모드 효율성: 마지막 변경분만 처리하므로 대용량 테이블에서도 효율적입니다.
    • 벤더 중립: 특정 클라우드나 플랫폼에 종속되지 않습니다.

    단점 및 한계

    • MoR(Merge-on-Read) 미지원: Hudi와 Iceberg의 MoR 테이블은 지원하지 않습니다. 로그 파일이 동기화되지 않아 데이터가 불완전하게 보일 수 있습니다.
    • Delta Delete Vectors 미지원: Delta Lake의 Delete Vectors가 동기화되지 않습니다.
    • 쓰기 최적화 손실: Secondary 포맷 고유의 쓰기 최적화(Iceberg의 Hidden Partitioning, Delta의 Deletion Vectors 등)가 적용되지 않습니다.
    • Generated Columns 제한: Delta Lake의 Generated Columns 동기화가 제한적입니다.
    • 타임스탬프 불일치: Secondary 포맷 커밋 타임스탬프가 Primary와 정확히 일치하지 않을 수 있습니다.
    • 구조화 데이터 전용: 비정형 데이터는 지원하지 않습니다.
    • Hudi 요구사항: Hudi를 소스로 사용할 경우 버전 0.14.0 이상, 메타데이터 테이블 및 Hive 스타일 파티셔닝 활성화가 필요합니다.
    • 운영 오버헤드: 별도 JAR 실행 및 동기화 스케줄 관리가 필요합니다.

    Delta Lake Uniform과의 비교

    Delta Lake Uniform은 XTable과 자주 비교되는 기능입니다. 차이를 명확히 이해하고 선택해야 합니다.

    항목 Apache XTable Delta Lake Uniform
    변환 방향 전방향 (Hudi↔Iceberg↔Delta) 단방향 (Delta → Iceberg/Hudi)
    소스 포맷 Hudi, Iceberg, Delta 모두 가능 Delta Lake만 소스 가능
    Databricks 의존성 없음 (독립 실행) Databricks 플랫폼에 통합
    운영 방식 별도 JAR 실행 필요 Delta 쓰기 시 자동 동기화
    Liquid Clustering 제약 없음 함께 사용 불가
    적합한 케이스 포맷 간 마이그레이션, 다중 포맷 유지, Hudi 중심 환경 Databricks에서 쓰고 Snowflake에서 읽는 단순 패턴

    Apache XTable와 비슷한 오픈소스 및 매니지드 서비스

    XTable과 Delta Uniform이 해결하는 문제는 "데이터 복사 없이 여러 테이블 포맷을 동시에 지원하는 것" 입니다. 이 관점에서 동일한 문제를 다루는 도구들을 정리합니다.

    포맷 변환/번역 도구 (직접 비교 대상)

    이름 방향 특징
    Delta Lake Uniform Delta → Iceberg / Hudi (단방향) Databricks에 내장. Delta 쓰기 시 자동 동기화. 별도 비교 섹션 참고.
    Iceberg Snapshot / Migrate 프로시저 Parquet · Hive · Delta → Iceberg (단방향) Iceberg 자체 내장 기능. 운영 중 지속 동기화가 아닌 1회성 마이그레이션에 적합.
    Hudi Sync Tool (HoodieHiveSync 등) Hudi → Hive Metastore / Iceberg (단방향) Hudi 내장. 커밋마다 외부 카탈로그에 메타데이터를 자동 등록. XTable보다 범위가 좁음.

    다른 접근법으로 같은 문제 해결 (쿼리 레이어 통합)

    메타데이터를 번역하는 대신, 쿼리 엔진 레이어에서 여러 포맷을 직접 해석하는 방식입니다.

    이름 설명
    Dremio Iceberg, Delta, Hudi, Parquet 등을 단일 쿼리 레이어로 통합. 포맷 변환 없이 다중 포맷 동시 접근.
    Starburst / Trino Trino 기반으로 다양한 포맷 및 데이터 소스를 단일 SQL로 조회.
    Apache Spark Hudi / Iceberg / Delta 모두 읽기 지원. 변환 없이 쿼리만으로 포맷 간 접근 가능.

    매니지드 서비스 (XTable 기반 또는 포맷 상호운용 지원)

    이름 설명
    Onehouse XTable을 만든 회사의 매니지드 레이크하우스 서비스. XTable 동기화, 테이블 최적화, 멀티 포맷 수집을 완전 관리형으로 제공. AWS · GCP · Azure 지원.
    AWS EMR + Glue EMR에서 XTable JAR를 실행하고 Glue Catalog에 멀티 포맷 테이블을 등록하는 조합으로 매니지드에 가깝게 운영 가능.

    사용 예시

    예시 1: Hudi → Iceberg + Delta 동시 동기화

    Hudi로 수집한 스트리밍 데이터를 Dremio(Iceberg)와 Databricks(Delta) 양쪽에서 동시에 읽어야 하는 경우입니다.

    1단계: Hudi 테이블 생성 (PySpark)

    # pyspark --packages org.apache.hudi:hudi-spark3.2-bundle_2.12:0.14.0
    
    from pyspark.sql.types import *
    
    table_name = "people"
    base_path  = "s3://my-bucket/hudi-dataset"
    
    records = [
        (1, 'Alice', 25, 'Seoul', '2024-01-01 00:00:00'),
        (2, 'Bob',   30, 'Busan', '2024-01-01 00:00:00'),
    ]
    schema = StructType([
        StructField("id",        IntegerType(), True),
        StructField("name",      StringType(),  True),
        StructField("age",       IntegerType(), True),
        StructField("city",      StringType(),  True),
        StructField("create_ts", StringType(),  True),
    ])
    
    df = spark.createDataFrame(records, schema)
    
    hudi_options = {
        'hoodie.table.name': table_name,
        'hoodie.datasource.write.partitionpath.field': 'city',
        'hoodie.datasource.write.hive_style_partitioning': 'true',
    }
    
    df.write.format("hudi").options(**hudi_options).save(f"{base_path}/{table_name}")

    2단계: XTable 동기화 설정 (my_config.yaml)

    sourceFormat: HUDI
    targetFormats:
      - DELTA
      - ICEBERG
    datasets:
      - tableBasePath: s3://my-bucket/hudi-dataset/people
        tableName: people
        partitionSpec: city:VALUE

    3단계: 동기화 실행

    java -jar xtable-utilities_2.12-0.2.0-SNAPSHOT-bundled.jar \
      --datasetConfig my_config.yaml

    실행 후 s3://my-bucket/hudi-dataset/people/ 아래에 _delta_log/metadata/ 디렉토리가 생성됩니다. 데이터 파일은 변경되지 않습니다.


    예시 2: Delta → Iceberg 마이그레이션 (무중단)

    기존 Delta Lake 테이블을 Iceberg로 전환 중이며, 전환 기간 동안 두 포맷을 모두 지원해야 하는 경우입니다.

    sourceFormat: DELTA
    targetFormats:
      - ICEBERG
    datasets:
      - tableBasePath: s3://my-bucket/delta-dataset/sales
        tableName: 2024_sales
        partitionSpec: partitionpath:department

    Dremio에서 Iceberg 테이블 등록

    CALL dremio.system.register_table(
      table        => 'analytics.sales_2024',
      metadata_file => 's3://my-bucket/delta-dataset/sales/metadata/v1.metadata.json'
    );

    예시 3: 실시간 스트리밍 파이프라인 패턴

    Hudi로 실시간 업서트를 수행하고, 다운스트림 소비는 Iceberg로 제공하는 패턴입니다.

    [Kafka] → [Spark Structured Streaming] → [Hudi 쓰기 (low-latency upsert)]
                                                           ↓
                                            [XTable 증분 동기화 (스케줄 실행)]
                                                           ↓
                                              [Iceberg 메타데이터 생성]
                                                           ↓
                                  [Snowflake / Dremio / AWS Athena 에서 읽기]

    이 패턴을 통해 Hudi의 고속 수집 능력과 Iceberg의 넓은 쿼리 엔진 지원을 동시에 활용할 수 있습니다.


    예시 4: Iceberg → Delta Lake 변환

    Apache Iceberg로 운영 중인 테이블을 Databricks에서도 읽어야 하는 경우입니다. Iceberg를 Primary로 유지하면서 Delta Lake 메타데이터를 동기화합니다.

    1단계: Iceberg 테이블 생성 (PySpark)

    pyspark \
      --packages org.apache.iceberg:iceberg-spark-runtime-3.2_2.12:1.4.1 \
      --conf "spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions" \
      --conf "spark.sql.catalog.spark_catalog=org.apache.iceberg.spark.SparkSessionCatalog" \
      --conf "spark.sql.catalog.spark_catalog.type=hadoop" \
      --conf "spark.sql.catalog.spark_catalog.warehouse=s3://my-bucket/iceberg-warehouse"
    from pyspark.sql.types import *
    
    table_name = "orders"
    warehouse  = "s3://my-bucket/iceberg-warehouse"
    
    records = [
        (1001, 'Alice', 'laptop',   1200000, '2024-03-01'),
        (1002, 'Bob',   'monitor',   450000, '2024-03-01'),
        (1003, 'Carol', 'keyboard',   89000, '2024-03-02'),
    ]
    schema = StructType([
        StructField("order_id",   IntegerType(), True),
        StructField("customer",   StringType(),  True),
        StructField("product",    StringType(),  True),
        StructField("amount",     IntegerType(), True),
        StructField("order_date", StringType(),  True),
    ])
    
    df = spark.createDataFrame(records, schema)
    
    df.write \
      .format("iceberg") \
      .partitionBy("order_date") \
      .saveAsTable(f"spark_catalog.default.{table_name}")

    실행 후 s3://my-bucket/iceberg-warehouse/default/orders/ 아래에 metadata/data/가 생성됩니다.

    2단계: XTable 동기화 설정 (my_config.yaml)

    sourceFormat: ICEBERG
    targetFormats:
      - DELTA
    datasets:
      - tableBasePath: s3://my-bucket/iceberg-warehouse/default/orders
        tableDataPath: s3://my-bucket/iceberg-warehouse/default/orders/data
        tableName: orders

    ⚠️ tableDataPath 필수: Iceberg는 메타데이터(metadata/)와 데이터(data/) 디렉토리가 분리되어 있으므로 반드시 tableDataPath를 명시해야 합니다.

    3단계: 동기화 실행

    java -jar xtable-utilities_2.12-0.2.0-SNAPSHOT-bundled.jar \
      --datasetConfig my_config.yaml

    실행 후 같은 경로에 _delta_log/가 추가됩니다. Parquet 데이터 파일은 변경되지 않습니다.

    4단계: Databricks에서 Delta로 읽기

    # Databricks 노트북에서 실행
    
    # 방법 1: 경로 직접 지정
    df = spark.read.format("delta").load(
        "s3://my-bucket/iceberg-warehouse/default/orders"
    )
    df.show()
    
    # 방법 2: 테이블로 등록 후 SQL 사용
    spark.sql("""
      CREATE TABLE IF NOT EXISTS orders_via_delta
      USING DELTA
      LOCATION 's3://my-bucket/iceberg-warehouse/default/orders'
    """)
    
    spark.sql("""
      SELECT * FROM orders_via_delta WHERE order_date = '2024-03-01'
    """).show()

    동일한 S3 경로에 Iceberg와 Delta 메타데이터가 공존하며, Parquet 파일은 하나만 존재합니다.


    XTable 운영 가이드

    XTable 자체는 단순한 CLI 도구이지만, 프로덕션 환경에서는 주기적 실행, 카탈로그 등록, 모니터링을 어떻게 설계할지가 중요합니다.

    실행 모드

    모드 방식 적합한 상황
    일회성 실행 java -jar xtable.jar --datasetConfig config.yaml 초기 마이그레이션, 수동 테스트
    스케줄 실행 cron / Airflow / EventBridge 등으로 주기 호출 배치성 파이프라인, 허용 지연이 분 단위 이상인 경우
    연속 실행 (RunSync) 수집 태스크 직후 자동 트리거 수집 후 즉시 동기화가 필요한 스트리밍 파이프라인

    운영 패턴 1: cron 스케줄 (가장 단순)

    # crontab -e
    
    # 15분마다 증분 동기화
    */15 * * * * java -jar /opt/xtable/xtable.jar \
      --datasetConfig /etc/xtable/config.yaml \
      >> /var/log/xtable/sync.log 2>&1

    배치성 파이프라인이 실행되는 EMR, Dataproc 등의 워커 노드에 배포하는 가장 단순한 패턴입니다. 동기화 주기는 데이터 신선도 요구 수준에 맞춰 조정합니다.

    운영 패턴 2: AWS Lambda + EventBridge (서버리스)

    서버 관리 없이 자동화하는 권장 패턴입니다.

    [EventBridge 스케줄 (1시간마다)]
              ↓
    [Detector Lambda] → Glue Catalog 스캔
      (xtable_table_type, xtable_target_formats 태그 보유 테이블 탐지)
              ↓ 비동기 호출
    [Converter Lambda × N개 테이블]
      각 테이블별로 XTable JAR 실행
              ↓
    [S3에 Secondary 포맷 메타데이터 생성 + Glue Catalog 자동 등록]

    Glue 테이블에 XTable 태그 설정 (테이블별 1회)

    glue_client.update_table(
        DatabaseName='my_db',
        TableInput={
            'Name': 'orders',
            'Parameters': {
                'xtable_table_type':     'ICEBERG',   # Primary 포맷
                'xtable_target_formats': 'DELTA',     # 변환 대상
            }
        }
    )

    Detector Lambda가 Glue를 스캔하여 위 태그가 있는 테이블만 자동으로 변환합니다. 새 테이블 추가 시 태그만 달면 이후 자동 관리됩니다.

    운영 패턴 3: Apache Airflow DAG

    데이터 파이프라인이 Airflow로 관리되는 환경에서 XTable 동기화를 파이프라인에 통합하는 패턴입니다.

    from airflow import DAG
    from airflow.operators.bash import BashOperator
    from datetime import datetime
    
    with DAG(
        dag_id='xtable_sync',
        schedule_interval='*/30 * * * *',  # 30분마다
        start_date=datetime(2024, 1, 1),
        catchup=False,
    ) as dag:
    
        sync_orders = BashOperator(
            task_id='sync_orders_iceberg_to_delta',
            bash_command=(
                'java -jar /opt/xtable/xtable.jar '
                '--datasetConfig /opt/xtable/configs/orders.yaml'
            ),
        )
    
        sync_users = BashOperator(
            task_id='sync_users_hudi_to_iceberg',
            bash_command=(
                'java -jar /opt/xtable/xtable.jar '
                '--datasetConfig /opt/xtable/configs/users.yaml'
            ),
        )
    
        # 두 테이블은 독립적이므로 병렬 실행
        [sync_orders, sync_users]

    수집(ingest) 태스크 다음에 XTable 동기화 태스크를 연결하면 수집 완료 후 즉시 동기화를 보장할 수 있습니다.

    증분(Incremental) vs 전체(Full) 동기화

    구분 동작 사용 시점
    Incremental 마지막 동기화 이후 새 커밋만 처리 일반 운영 (기본값)
    Full 전체 커밋 재처리 Incremental 실패 시 자동 폴백, 또는 테이블 재구성 시 수동 실행

    XTable은 Incremental 실패 시 자동으로 Full로 폴백하므로 별도 예외 처리 코드가 필요하지 않습니다.

    카탈로그 동기화 (CatalogSync)

    XTable은 메타데이터 파일 생성 후 외부 카탈로그에 자동 등록하는 CatalogSync도 지원합니다.

    카탈로그 지원 포맷 비고
    AWS Glue Iceberg, Delta, Hudi catalogImpl: glue 설정
    Hive Metastore Iceberg, Delta, Hudi HMS 주소 + catalogImpl: hms 설정
    Unity Catalog 로드맵 예정 현재 미지원

    카탈로그 동기화를 활성화하면 Athena, Spark SQL, Trino 등에서 수동 등록 없이 즉시 조회할 수 있습니다.

    모니터링 포인트

    S3에서 메타데이터 생성 확인

    # Iceberg → Delta 동기화 후 Delta 메타데이터 존재 확인
    aws s3 ls s3://my-bucket/iceberg-warehouse/default/orders/_delta_log/
    
    # Hudi → Iceberg 동기화 후 Iceberg 메타데이터 확인
    aws s3 ls s3://my-bucket/hudi-dataset/people/metadata/

    주요 경보 조건

    • _delta_log/ 또는 metadata/가 예상 주기보다 오래 업데이트되지 않는 경우 → XTable 프로세스 상태 확인
    • Incremental 실패로 Full Sync 폴백이 반복되는 경우 → 커밋 히스토리 크기 및 실행 시간 검토
    • Primary 포맷 커밋 수와 Secondary 커밋 수의 차이가 커지는 경우 → 동기화 주기 조정

    운영 시 주의사항

    • Secondary 포맷으로는 절대 쓰지 않습니다. XTable이 생성한 메타데이터를 외부 도구가 덮어쓰면 데이터 일관성이 깨집니다.
    • Full Sync 비용 관리: Full Sync는 커밋 수에 비례해 시간이 소요됩니다. 일반 운영은 Incremental로만 사용하고, Full은 장애 복구 시에만 수동 실행하는 것이 권장됩니다.
    • MoR 테이블 회피: Hudi/Iceberg의 Merge-on-Read 테이블은 XTable에서 지원하지 않습니다. CoW로 설정하거나 별도 전략을 검토하십시오.
    • 첫 동기화 후 검증 필수: 변환된 포맷으로 실제 쿼리를 실행하여 데이터 정합성을 확인하는 절차를 반드시 거치십시오.

    요약

    Apache XTable은 데이터 레이크하우스의 포맷 파편화 문제를 메타데이터 변환만으로 해결하는 도구입니다.

    항목 내용
    핵심 가치 데이터 복사 없이 Hudi↔Iceberg↔Delta 상호 운용
    동작 방식 Primary 포맷으로 쓰기 → XTable이 Secondary 포맷 메타데이터 생성
    적합한 케이스 멀티 포맷 팀, 포맷 마이그레이션, 스트리밍+분석 혼합 환경
    주의 사항 MoR 테이블, Delete Vectors, Generated Columns 미지원
    대안 Delta Uniform(단방향), Iceberg Migrate, Dremio/Trino
    현재 상태 Apache Software Foundation Incubating 프로젝트

    언제 XTable을 선택해야 할까요?

    • 조직 내 여러 팀이 서로 다른 레이크하우스 포맷을 사용하는 경우
    • 포맷을 전환하면서도 다운타임 없이 기존 워크로드를 유지해야 하는 경우
    • Hudi로 수집하고 Iceberg/Delta로 분석하는 패턴이 필요한 경우
    • 특정 벤더에 종속되지 않고 여러 플랫폼에서 동일한 데이터에 접근해야 하는 경우

    댓글