iceberg

iceberg

Amazon S3, Azure, HDFS 또는 로컬에 저장된 Apache Iceberg 테이블에 테이블 같은 인터페이스를 제공하는 테이블 함수예요. 테이블 엔진 형태와 함께 Iceberg 테이블을 읽는 방법을 제공해요.

출처: 문서

본문

Amazon S3, Azure, HDFS 또는 로컬에 저장된 Apache Iceberg 테이블에 테이블 같은 인터페이스를 제공해요.

문법 (Syntax)

icebergS3(url [, NOSIGN | access_key_id, secret_access_key, [session_token]] [,format] [,compression_method] [,extra_credentials])
icebergS3(named_collection[, option=value [,..]])

icebergAzure(connection_string|storage_account_url, container_name, blobpath, [,account_name], [,account_key] [,format] [,compression_method])
icebergAzure(named_collection[, option=value [,..]])

icebergHDFS(path_to_table, [,format] [,compression_method])
icebergHDFS(named_collection[, option=value [,..]])

icebergLocal(path_to_table, [,format] [,compression_method])
icebergLocal(named_collection[, option=value [,..]])

인자 (Arguments)

인자의 설명은 각각 s3, azureBlobStorage, HDFS, file 테이블 함수의 인자 설명과 일치해요.

format은 Iceberg 테이블의 데이터 파일 포맷을 뜻해요.

icebergS3의 경우 선택 사항인 extra_credentials 파라미터로 ClickHouse Cloud에서 역할 기반 접근을 위한 role_arn을 전달할 수 있어요. 구성 단계는 Secure S3를 참고하세요.

반환값 (Returned value)

지정된 Iceberg 테이블의 데이터를 읽을 수 있는, 지정된 구조를 가진 테이블이에요.

예시 (Example)

SELECT * FROM icebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')

ClickHouse는 icebergS3, icebergAzure, icebergHDFS, icebergLocal 테이블 함수와 IcebergS3, IcebergAzure, IcebergHDFS, IcebergLocal 테이블 엔진을 통해 Iceberg 포맷의 v1과 v2를 읽을 수 있어요. v3 지원은 부분적이에요: 삭제 벡터(deletion vector) 읽기는 지원하지만, 매니페스트 컴팩션(manifest compaction)은 지원하지 않아요.

네임드 컬렉션 정의 (Defining a named collection)

URL과 자격 증명을 저장하기 위한 네임드 컬렉션을 구성하는 예시예요:

<clickhouse>
 <named_collections>
  <iceberg_conf>
   <url>http://test.s3.amazonaws.com/clickhouse-bucket/</url>
   <access_key_id>test</access_key_id>
   <secret_access_key>test</secret_access_key>
   <format>auto</format>
   <structure>auto</structure>
  </iceberg_conf>
 </named_collections>
</clickhouse>
SELECT * FROM icebergS3(iceberg_conf, filename = 'test_table')
DESCRIBE icebergS3(iceberg_conf, filename = 'test_table')

데이터 카탈로그 사용 (Using a data catalog)

Iceberg 테이블은 REST Catalog, AWS Glue Data Catalog, Unity Catalog 같은 다양한 데이터 카탈로그와 함께 사용할 수도 있어요.

카탈로그를 사용할 때 대부분의 사용자는 ClickHouse를 카탈로그에 연결해 테이블을 발견하게 해주는 DataLakeCatalog 데이터베이스 엔진을 쓰고 싶어할 거예요. 이 데이터베이스 엔진을 사용하면 IcebergS3 테이블 엔진으로 개별 테이블을 수동으로 만들지 않아도 돼요.

사용하려면 IcebergS3 엔진으로 테이블을 만들고 필요한 설정을 제공하면 돼요.

예를 들어 MinIO 스토리지와 REST Catalog를 사용하는 경우:

CREATE TABLE `database_name.table_name`
ENGINE = IcebergS3(
 'http://minio:9000/warehouse-rest/table_name/',
 'minio_access_key',
 'minio_secret_key'
)

또는 S3와 AWS Glue Data Catalog를 사용하는 경우:

CREATE TABLE `my_database.my_table` 
ENGINE = IcebergS3(
 's3://my-data-bucket/warehouse/my_database/my_table/',
 'aws_access_key',
 'aws_secret_key'
)

스키마 진화 (Schema Evolution)

현재 ClickHouse를 사용하면 시간에 따라 스키마가 변경된 Iceberg 테이블을 읽을 수 있어요. 컬럼이 추가·제거되었거나 그 순서가 변경된 테이블을 읽는 것을 지원해요. 값이 필수인 컬럼을 NULL이 허용되는 컬럼으로 바꿀 수도 있어요. 또한 단순 타입에 대해 허용되는 타입 캐스팅을 지원해요:

  • int -> long
  • float -> double
  • decimal(P, S) -> decimal(P', S) (여기서 P' > P)

현재 중첩 구조 또는 배열·맵 내부 요소의 타입을 바꾸는 것은 불가능해요.

파티션 프루닝 (Partition Pruning)

ClickHouse는 Iceberg 테이블의 SELECT 쿼리 중 파티션 프루닝을 지원해요. 관련 없는 데이터 파일을 건너뛰어 쿼리 성능을 최적화하는 데 도움이 돼요. 파티션 프루닝을 활성화하려면 use_iceberg_partition_pruning = 1을 설정하세요. Iceberg 파티션 프루닝에 대한 자세한 내용은 https://iceberg.apache.org/spec/#partitioning을 참고하세요.

타임 트래블 (Time Travel)

ClickHouse는 Iceberg 테이블의 타임 트래블을 지원해요. 특정 타임스탬프나 스냅샷 ID로 과거 데이터를 조회할 수 있어요.

삭제된 행이 있는 테이블 처리 (Processing of tables with deleted rows)

ClickHouse는 포지션 삭제(position deletes)동등 삭제(equality deletes)가 있는 Iceberg 테이블을 지원해요. 동등 삭제는 v25.8부터 지원돼요.

ClickHouse는 삭제 벡터(deletion vectors)(v3에 도입) 읽기도 지원해요. 이 지원은 읽기 전용이에요: ClickHouse는 삭제 벡터를 쓰거나, 갱신하거나, 컴팩션하지 않으며, Iceberg 포맷 버전 3 테이블에서는 ALTER TABLE ... DELETEALTER TABLE ... UPDATE를 지원하지 않아요.

기본 사용법 (Basic usage)

SELECT * FROM example_table ORDER BY 1 
SETTINGS iceberg_timestamp_ms = 1714636800000
SELECT * FROM example_table ORDER BY 1 
SETTINGS iceberg_snapshot_id = 3547395809148285433

참고: 같은 쿼리에서 iceberg_timestamp_msiceberg_snapshot_id 파라미터를 동시에 지정할 수는 없어요.

중요한 고려사항 (Important considerations)

스냅샷(Snapshot) 은 보통 다음과 같은 때 만들어져요:

  • 테이블에 새 데이터가 쓰일 때
  • 어떤 종류의 데이터 컴팩션이 수행될 때

스키마 변경은 보통 스냅샷을 만들지 않아요 — 이 때문에 스키마 진화를 겪은 테이블에서 타임 트래블을 사용할 때 중요한 동작이 생겨요.

예시 시나리오 (Example scenarios)

이 시나리오들은 외부 Iceberg 작성자가 만든 스키마 변경을 설명하기 위해 Spark를 사용해요.

시나리오 1: 새 스냅샷 없는 스키마 변경 (Schema Changes Without New Snapshots)

다음과 같은 작업 순서를 생각해볼게요:

-- 두 개의 컬럼을 가진 테이블 생성
 CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example (
  order_number bigint, 
  product_code string
 ) 
 USING iceberg 
 OPTIONS ('format-version'='2')

- - 테이블에 데이터 삽입
 INSERT INTO spark_catalog.db.time_travel_example VALUES 
 (1, 'Mars')

 ts1 = now() // 의사 코드 조각

- - 새 컬럼을 추가하도록 테이블 변경
 ALTER TABLE spark_catalog.db.time_travel_example ADD COLUMN (price double)

 ts2 = now()

- - 테이블에 데이터 삽입
 INSERT INTO spark_catalog.db.time_travel_example VALUES (2, 'Venus', 100)

 ts3 = now()

- - 각 타임스탬프에서 테이블 조회
 SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts1;

+------------+------------+
|order_number|product_code|
+------------+------------+
| 1| Mars|
+------------+------------+
 SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts2;

+------------+------------+
|order_number|product_code|
+------------+------------+
| 1| Mars|
+------------+------------+

 SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts3;

+------------+------------+-----+
|order_number|product_code|price|
+------------+------------+-----+
| 1| Mars| NULL|
| 2| Venus|100.0|
+------------+------------+-----+

타임스탬프별 조회 결과:

  • ts1과 ts2에서: 원래 두 컬럼만 나타나요
  • ts3에서: 세 컬럼이 모두 나타나고, 첫 행의 price는 NULL이에요

시나리오 2: 과거 스키마와 현재 스키마의 차이 (Historical vs. Current Schema Differences)

현재 시점의 타임 트래블 쿼리는 현재 테이블과 다른 스키마를 보여줄 수 있어요:

-- 테이블 생성
 CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_2 (
  order_number bigint, 
  product_code string
 ) 
 USING iceberg 
 OPTIONS ('format-version'='2')

-- 테이블에 초기 데이터 삽입
 INSERT INTO spark_catalog.db.time_travel_example_2 VALUES (2, 'Venus');

-- 새 컬럼을 추가하도록 테이블 변경
 ALTER TABLE spark_catalog.db.time_travel_example_2 ADD COLUMN (price double);

 ts = now();

-- 타임스탬프 문법으로 현재 시점을 조회

 SELECT * FROM spark_catalog.db.time_travel_example_2 TIMESTAMP AS OF ts;

 +------------+------------+
 |order_number|product_code|
 +------------+------------+
 | 2| Venus|
 +------------+------------+

-- 현재 시점을 조회
 SELECT * FROM spark_catalog.db.time_travel_example_2;
 +------------+------------+-----+
 |order_number|product_code|price|
 +------------+------------+-----+
 | 2| Venus| NULL|
 +------------+------------+-----+

이런 일이 생기는 이유는 ALTER TABLE이 새 스냅샷을 만들지 않는데, 현재 테이블에 대해 Spark는 스냅샷이 아니라 최신 메타데이터 파일에서 schema_id 값을 가져오기 때문이에요.

시나리오 3: 과거 스키마와 현재 스키마의 차이 (Historical vs. Current Schema Differences)

두 번째는 타임 트래블을 할 때 데이터가 전혀 쓰이지 않은 테이블의 상태를 얻을 수 없다는 점이에요:

-- 테이블 생성
 CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_3 (
  order_number bigint, 
  product_code string
 ) 
 USING iceberg 
 OPTIONS ('format-version'='2');

 ts = now();

-- 특정 타임스탬프에서 테이블 조회
 SELECT * FROM spark_catalog.db.time_travel_example_3 TIMESTAMP AS OF ts; -- ts보다 오래된 스냅샷을 찾을 수 없어 오류가 납니다.

ClickHouse의 동작은 Spark와 일관돼요. Spark Select 쿼리를 ClickHouse Select 쿼리로 바꿔 생각하면 같은 방식으로 동작해요.

메타데이터 파일 해석 (Metadata File Resolution)

ClickHouse에서 iceberg 테이블 함수를 사용할 때 시스템은 Iceberg 테이블 구조를 설명하는 올바른 metadata.json 파일을 찾아야 해요. 이 해석 과정의 동작은 다음과 같아요.

후보 탐색 (우선순위 순)

  1. 직접 경로 지정 (Direct Path Specification): iceberg_metadata_file_path를 설정하면 시스템은 이 경로를 Iceberg 테이블 디렉터리 경로와 결합하여 정확히 사용해요. 이 설정이 제공되면 다른 모든 해석 설정은 무시돼요.
  2. 테이블 UUID 일치 (Table UUID Matching): iceberg_metadata_table_uuid를 지정하면 시스템은 metadata 디렉터리 안의 .metadata.json 파일만 보고, 지정한 UUID와 일치하는(대소문자 무시) table-uuid 필드를 포함한 파일만 필터링해요.
  3. 기본 탐색 (Default Search): 위 두 설정이 모두 제공되지 않으면 metadata 디렉터리의 모든 .metadata.json 파일이 후보가 돼요.

가장 최근 파일 선택 (Selecting the Most Recent File)

위 규칙으로 후보 파일을 찾은 뒤 시스템은 가장 최근 파일을 다음과 같이 판단해요:

  • iceberg_recent_metadata_file_by_last_updated_ms_field가 활성화된 경우: last-updated-ms 값이 가장 큰 파일을 선택해요.
  • 그렇지 않은 경우: 버전 번호가 가장 높은 파일을 선택해요 (버전은 V.metadata.json 또는 V-uuid.metadata.json 파일 이름에서 V로 나타나요).

참고: 언급한 모든 설정은 테이블 함수 설정(전역 또는 쿼리 수준 설정이 아님)이며, 아래와 같이 지정해야 해요:

SELECT * FROM iceberg('s3://bucket/path/to/iceberg_table', 
 SETTINGS iceberg_metadata_table_uuid = 'a90eed4c-f74b-4e5b-b630-096fb9d09021');

참고: Iceberg 카탈로그는 보통 메타데이터 해석을 처리하지만, ClickHouse의 iceberg 테이블 함수는 S3에 저장된 파일을 Iceberg 테이블로 직접 해석하므로 이러한 해석 규칙을 이해하는 것이 중요해요.

메타데이터 캐시 (Metadata cache)

Iceberg 테이블 엔진과 테이블 함수는 매니페스트 파일, 매니페스트 목록, 메타데이터 json의 정보를 저장하는 메타데이터 캐시를 지원해요. 캐시는 메모리에 저장돼요. 이 기능은 기본적으로 활성화된 use_iceberg_metadata_files_cache 설정으로 제어돼요.

별칭 (Aliases)

테이블 함수 iceberg는 이제 icebergS3의 별칭이에요.

가상 컬럼 (Virtual Columns)

  • _path — 파일의 경로예요. 타입: LowCardinality(String).
  • _file — 파일의 이름이에요. 타입: LowCardinality(String).
  • _size — 바이트 단위의 파일 크기예요. 타입: Nullable(UInt64). 파일 크기를 알 수 없으면 값은 NULL이에요.
  • _time

더 알아보기 (Learn more)