안녕하세요? 박민재입니다. 오늘은 Iceberg Table을 관리하는 방법을 Metadata Table의 사용을 중심으로 깊게 알아 보도록 하겠습니다.
Apache Iceberg의 경우에는 Metadata Table 기능을 매우 강력하게 지원합니다. 이를 통해 Iceberg Table을 운영을 쉽게 수행 할 수 있죠. 예를 들어, Table의 Evolution이 어떻게 진행 되었는지, 파일들이 어떻게 Partitioning 되어 운영 되는지, Partition별 File의 갯수, 특정 File 내에서 Column의 Upper Bound, Lower Bound 등이 어떻게 되는지 등을 기록할 수 있어요.
또한 메타데이터를 SHOW PARTITION 같은 Hive Dialect가 아닌, 대부분의 SQL 엔진에서 사용할 수 있는 형태로 제공 하고 있고요.
SELECT * FROM catalog.table.history AS OF VERSION 1059035530770364194
크게 다음과 같은 Metadata Table 들을 지원하고 있습니다.
history
Metadata Table은 Table이 어떻게 evolution 되었는지 History를 기록하는 Table입니다.
Snapshot 정보를 기록 하며, 이는 데이터 복구 및 버전 제어에 사용 할 수 있습니다. 해당 스냅샷에 대해서 SQL 쿼리를 수행 하는 형식으로 사용합니다.
아래는 history
테이블에 특정 시점 이전의 Snapshot에 대해 쿼리 하는 예제 입니다.
SELECT snapshot_id
FROM catalog.table.metadata_log_entries
WHERE made_current_at < '2023-07-11 00:00:00'
ORDER BY made_current_at ASC
이를 통해서 Rollback이 수행 되었는지 여부도 도출이 가능합니다.
parent_id를
가지고 있는가?is_current_ancestor
가 true
인가? (Current Snapshot의 조상 인지 여부. 만약, 롤백 이후 다시 Snapshot이 생성 되었다면, 하나만 Current Snapshot의 조상에 해당 할 것)metadata_log_entries는 Table Update 중에 생성된 Metadata File을 로깅하여 Table Evolution을 추적 합니다. 이를 통해, snapshot이 어떤 시점의 Metadata File들을 가지고 있었는지 확인 할 수 있습니다.
응용하는 예시로는, Schema 변경이 일어나기 직전의 최근 Snapshot을 추출하여, Schema 변경 전으로 Rollback 하는 데 사용하는 예제가 있습니다.
WITH Ranked_Entries AS (
SELECT
latest_snapshot_id,
latest_schema_id,
timestamp,
ROW_NUMBER() OVER(PARTITION BY latest_schema_id ORDER BY timestamp DESC) as row_num
FROM
catalog.table.metadata_log_entries
WHERE
latest_schema_id IS NOT NULL
)
SELECT
latest_snapshot_id,
latest_schema_id,
timestamp AS latest_timestamp
FROM
Ranked_Entries
WHERE
row_num = 1
ORDER BY
latest_schema_id DESC;
snapshots
는 Snapshot의 정보를 담고 있는 테이블 입니다. Version 관리, Time Travel, 증분 처리, 과거 추적 등등을 수행 할 수 있습니다.
해당 Query를 통해서, Table 내에 있는 Snapshot ID 별로 몇 개의 Record 들이 추가 되었는지 확인 할 수 있습니다. 혹은, 하루에 몇 개의 Snapshot이 만들어 졌는지도 확인 할 수 있어요.
SELECT
committed_at,
snapshot_id,
summary['added-records'] AS added_records
FROM
catalog.table.snapshots;
SELECT
operation,
COUNT(*) AS operation_count,
DATE(committed_at) AS date
FROM
catalog.table.snapshots
GROUP BY
operation,
DATE(committed_at)
ORDER BY
date;
files
는 테이블 내의 현재 데이터 파일을 보여주며, 각 데이터 파일의 위치, 형식, 내용, 파티셔닝 정보등을 제공 합니다.
다른 Table의 정보들과 함께 이용하여 사용 하면, Data Lineage Tracking, Schema Evolution 등, 다양한 케이스를 대응 할 수 있습니다.
이를 통해 많은 것을 할 수 있는데요, 어떤 Partition을 Merge 하면 좋을지를 결정 하기 위해서, Partition별 평균 file_size를 측정 할 수도 있고요.
SELECT
partition,
COUNT(*) AS num_files,
AVG(file_size_in_bytes) AS avg_file_size
FROM
catalog.table.files
GROUP BY
partition
ORDER BY
num_files DESC,
avg_file_size ASC
혹은, 특정 Column에 null value가 있는 지도 확인 해 볼 수 있습니다.
SELECT
partition, file_path
FROM
catalog.table.files
WHERE
null_value_counts['3'] > 0
물론, 테이블 내의 File Total Size도 측정 할 수 있어요.
SELECT sum(file_size_in_bytes) from catalog.table.files
Time Travel 기능을 통해, 이전 Snapshot에서 가지고 있는 File의 목록 또한 한 눈에 볼 수 있습니다.
SELECT file_path, file_size_in_bytes
FROM catalog.table.files
VERSION AS OF <snapshot_id>;
유사한 Table인 all_data_files
가 있습니다. 이는 똑같은 Datafile로 스냅샷이 두 개 이상 만들어 졌다면 중복 될 수 있는 구조입니다.
file과 field가 같습니다.
manifests
Metadata Table은 Manifest File에 대해서 자세하게 기술 되어 있는 Metadata Table 입니다.
manifest를 압축 해야하는 경우에는 평균보다 낮은 Manifest File에 대해서 압축 하기 위해, 이를 추출하는 예제도 존재 합니다.
WITH avg_length AS (
SELECT AVG(length) as average_manifest_length
FROM catalog.table.manifests
)
SELECT
path,
length
FROM
catalog.table.manifests
WHERE
length < (SELECT average_manifest_length FROM avg_length);
데이터의 추가 및 삭제 추이도 확인 할 수 있습니다.
SELECT
added_snapshot_id,
SUM(added_data_files_count) AS total_added_data_files
FROM
catalog.table.manifests
GROUP BY
added_snapshot_id;
SELECT
added_snapshot_id
FROM
catalog.table.manifests
WHERE
deleted_data_files_count > 0;
이 또한, all_data_files
와 동일 하게 중복해서 나올 수 있는 구조 입니다. manifest는 여러 개의 Snapshot에 포함 될 수 있습니다.
partitions
는 Partition에 대한 정보를 나타냅니다.
다음과 같이, Partition 별 File Count를 확인 하거나
SELECT partition, file_count FROM catalog.table.partitions
File Size를 확인 하여, 추가 Partition Evolution이 필요한지에 대한 여부도 판단 할 수 있습니다.
SELECT partition, SUM(file_size_in_bytes) AS partition_size
FROM catalog.table.files
GROUP BY partition
Snapshot의 Reference를 기록 합니다. refs
는 특정 Snapshot의 Pointer로 간주가 되며, Snapshot을 Branching + Tagging 하기 위해서 사용합니다.
FYI) 추후 설명 할테지만, TAG / BRANCH에 Retention을 적용 할 수 있습니다.
Manifest의 entry로 사용 합니다. (Manifest → flie)
상기한 Metadata Table 들에 대해 Join하여 조회하는 방법으로, 원하는 데이터 들을 추출 할 수 있습니다.
다음 시간에는 Branching & Tagging에 대해서 알아 보도록 하겠습니다.