Iceberg distribution-mode

Q·2025년 9월 20일

Iceberg

목록 보기
10/14

Partition과 write.distribution-mode은 많이들 헷갈리기 쉬운 개념이라 이 둘의 차이와 실제 동작 방식을 Spark 예시와 함께 정리

1. Partition: 테이블의 논리적 분할

Partition은 테이블 데이터를 어떤 키 기준으로 나눌지를 정의

CREATE TABLE user_logs (
  ts TIMESTAMP,
  user_id BIGINT,
  country STRING
)
PARTITIONED BY (days(ts), country);

위와 같이 정의하면 Iceberg는 ts일(day) 단위로 나누고, 그 안에서 다시 country 값별로 디렉토리를 구성

  • 쿼리 프루닝(Pruning): 예를 들어 WHERE country='KR' AND ts='2025-09-01' 조건이 있으면 해당 파티션만 읽음.
  • 스냅샷 관리 단위: 파티션 단위로 데이터의 변경 이력을 관리

즉, Partition은 테이블 스키마 수준의 논리적 레이아웃

2. write.distribution-mode: 쓰기 시점의 물리적 분배 방식

Partition“데이터를 어떻게 나눌지”를 정의한다면, write.distribution-mode“같은 파티션 안에서 파일을 어떻게 생성할지”를 정의합니다.

주요 모드

  • none (기본값)

    • Spark 태스크(Task)가 처리하는 데이터를 그대로 파일로 저장
    • 빠르지만, 같은 파티션 안에 작은 파일이 여러 개 생김
    • Spark 업스트림 RDD/DataFrame partition 단위 그대로 Iceberg writer 로 들어감
    • 셔플 없음 → 기존 Spark partition 경계 유지
    • 따라서, Spark 의 물리적 partition 개수(= Task 개수)에 따라 파일 개수 ≈ Task 개수
    • Task 내부에서는 Iceberg 가 로컬 버퍼 사이즈(target file size) 기준으로 여러 파일로 자를 수 있음
    • 즉, Spark partition 기준으로 파일이 만들어짐
  • hash

    • 파티션 키를 기준으로 해시 분배
    • 동일한 파티션 키 값은 같은 태스크로 모여 하나의 큰 파일로 저장
    • 작은 파일 문제를 줄이고 쿼리 효율성 증가
    • Iceberg 가 분산(셔플) 을 한 번 더 걸어줌
    • 지정된 분산 키(보통 파티션 키, 또는 user 지정 column)의 해시 값을 기준으로 레코드를 Task 에 재분배
      • 이때 Spark 는 shuffle stage 를 새로 만들어서 Task → Reducer 재분배를 수행
      • 즉, 기존 Spark partition 경계는 무시됨 → Iceberg 가 새롭게 정의한 분배 규칙(hash)에 따라 Task 가 다시 생김
      • 최종 파일은 분배 키별 레코드 그룹 + target file size 기준으로 Task 가 나눈 결과
    • 즉, hash 모드에서는 Spark 의 원래 partition 수 ≠ 최종 파일 개수
    • Iceberg 가 셔플 후 Task 수/데이터 분포 에 따라 다시 결정
  • range

    • 정렬 가능한 컬럼 값 범위(Range)를 기준으로 파일 분배
    • 파티션 내부에서도 컬럼 값 순서대로 나누어 저장 → 범위 쿼리 성능 최적화
    • 셔플 발생
      • Iceberg 가 지정한 컬럼(주로 파티션 키 또는 특정 정렬 키)에 대해 범위 기반(range partitioning) 셔플을 실행
      • 예: event_time 기준으로 전체 데이터를 전역 정렬하려는 경우
    • Spark 는 새로운 shuffle stage 를 만들어서 데이터를 키의 범위 단위로 Task 에 분배
    • 각 Task 는 자신이 담당한 범위에 속하는 데이터만 받아서 정렬된 상태로 파일을 씀
    • 즉, 글로벌하게 정렬된 데이터 파일을 만들 수 있음

3. 예시 비교

Partition 예시

PARTITIONED BY (country)

→ country 값이 KR, US, JP일 경우 Iceberg는 각각 디렉토리를 만든다.

쓰기 시점 (write.distribution-mode 별 차이)

  • none
    • country=KR에 10만 건이 들어오고 Spark 태스크가 10개라면 → KR/part-0001.parquetKR/part-0010.parquet
    • 작은 파일 여러 개 생성
  • hash
    • country 컬럼 해시로 shuffle → 동일한 KR 값은 하나의 태스크로 모임
    • 결과: KR/part-0001.parquet (큰 파일 1개)
    • 파일 수 감소 → 쿼리 효율 ↑
  • range (예: user_id 기준)
    • country=KR 파티션 안에서도 user_id 범위별로 나눔
    • 예:
      • KR/user_id<10000.parquet
      • KR/10000<=user_id<20000.parquet
    • 범위 쿼리(WHERE user_id BETWEEN ...) 성능 최적화

4. Spark와의 비교

  • Spark 자체도 repartition, coalesce, sortWithinPartitions 같은 연산으로 파일 분배와 정렬을 제어할 수 있다.
  • Iceberg의 write.distribution-mode는 이런 Spark의 물리적 연산을 테이블 레벨에서 일관성 있게 보장해주는 개념이라고 이해할 수 있다.
  • 즉, Spark job마다 설정하지 않고 테이블 단위 정책으로 관리할 수 있다는 게 큰 장점
profile
Data Engineer

0개의 댓글