Всем привет! В этой статье хочу рассказать про то, как Iceberg работает под капотом, и про то, как он эффективно может взаимодействовать с данными через свою metadata.
Iceberg — табличный формат для больших аналитических наборов данных.
По сути, Iceberg — это прослойка между Data Lake и движками запросов, которая с помощью metadata позволяет движкам делать эффективные запросы.
философия iceberg
-
разделение data и metadata
-
атомарные обновления, консистентность и изоляция
-
time travel и branch
-
поддержка schema evolution и partition evolution
metadata iceberg
Metadata хранится в отдельной структуре, оптимизированной для чтения, что позволяет ускорять работу с данными без их полного переиндексирования.
Она состоит из набора таких элементов, как:
-
metadata.json
-
snapshot
-
manifest list
-
manifest file
-
data files
и служит для оптимизации запросов к Iceberg.
1. metadata.json
metadata.json хранит в себе версию таблицы и ссылки на другие элементы метаданных: схемы, список snapshot и список manifest-файлов.
По сути, является «таблицей версий» и consistency-файлом для всех читающих и пишущих процессов.
2. snapshot
snapshot — зафиксированное состояние таблицы в конкретный момент времени, которое определяет, какие файлы сейчас составляют таблицу.
В snapshot указана ссылка на manifest list.
3. manifest list
Перечисление всех manifest-файлов, которые относятся к конкретному snapshot.
Он нужен для облегчения чтения: вместо прохода по всем файлам Iceberg читает только те, которые реально относятся к запросу.
4. manifest file
Содержит ссылки на данные в рамках конкретного снимка:
-
путь к файлу
-
формат
-
размер
-
количество строк и сведения о разделах
-
статистики по столбцам
-
min
-
max
-
null-count
-
и прочие метрики
-
Благодаря manifest-метаданным можно проводить раннюю фильтрацию на уровне файлов, что снижает стоимость выполнения фильтров в движке запросов.
Схема чтения metadata
В момент исполнения запроса движок сначала читает текущий metadata.json -> выбирает нужный snapshot -> получает относящийся к выбранному snapshot manifest list -> подгружает manifest-файлы.

Благодаря структуре чтения metadata, Iceberg позволяет исключить наборы данных, не удовлетворяющие запросу.
какие проблемы решаются благодаря metadata
1. ACID
Это ключевые гарантии, которые должна обеспечивать БД:
-
Atomicity
-
Consistency
-
Isolation
-
Durability
Atomicity
Изменения проходят полностью либо вообще не проходят. Рассматривая Atomicity в OLTP-БД, мы видим, что БОЛЬШОЕ количество транзакций изменяет НЕБОЛЬШОЕ количество записей. Это связано с тем, что единицей транзакции является запись. В нашем случае Iceberg — это OLAP-нагрузка, и единицей транзакции является таблица.

Consistency
Все записи и изменения схем приводят к созданию нового snapshot и, следовательно, нового manifest list.
Тем самым Iceberg переводит базу данных из одного корректного состояния в другое.
Isolation
Читатели и писатели не мешают друг другу.
И каждый query видит консистентное состояние таблицы. Iceberg реализует это через snapshot isolation.
Проблема обычного Data Lake
-
Writer переписывает partition
dt=2026-04-01/ -
Reader в этот момент делает SELECT Он может увидеть: — часть старых файлов
-
часть новых
-
missing files Inconsistent table state.
-
Reader и Writer
-
создаёт новые data files
-
создаёт новый snapshot
-
атомарно переключает metadata pointer
Каждый query читает один конкретный snapshot.
пример: был snapshot 100 files: A, B Reader начал query. Writer создаёт snapshot 101 files: A, B, C Что увидит reader? Reader продолжит читать snapshot 100: A, B

Writer и Writer
Writer A создаёт snapshot 101 Writer B тоже начал от snapshot 100 Перед commit iceberg проверяет: изменилась ли таблица с начала моего query? Если изменилась, то commit B fail/retry.

Durability
Iceberg не отвечает за надёжное хранение данных.
За durability отвечает само файловое хранилище (HDFS, S3 и т. п.).
Time travel и branch
Функция time travel позволяет получить данные в том виде, в котором они были в конкретный момент времени, благодаря snapshot. Каждый snapshot представляет собой полную и согласованную версию таблицы на определённый момент времени.
Branch — это развитие идеи time travel, очень похожее на Git.
Обычный Time travel

последовательность snapshot, между которыми можно перемещаться.
Time travel с branch

Благодаря branch можно не только путешествовать в конкретное время, но и:
-
тестировать новые варианты хранения и работы с данными без влияния на производственные данные;
-
строить аналитику и отчётность с разными аналитическими моделями.
Поддержка schema evolution и partition evolution
Schema evolution и partition evolution в Iceberg работают без переписывания всей таблицы благодаря metadata. Она хранит не одну структуру таблицы, а историю версий схем и спецификаций партиций.
schema evolution
в схемах iceberg у каждой колонки есть immutable column ID Пример:
{ "name": "id", "id": 1 },{ "name": "name", "id": 2 }
И если мы хотим добавить колонку age, то наша новая схема будет выглядеть так:
{ "name": "id", "id": 1 },{ "name": "name", "id": 2 },{ "name": "age", "id": 3 }
И теперь при чтении старых файлов Iceberg понимает, что age = NULL для старых файлов.
rename column
Также в Iceberg можно просто переименовывать колонки, ведь мы просто меняем в metadata значение имени колонки. Пример: ДО:
{ "name": "id", "id": 1 },{ "name": "name", "id": 2 },{ "name": "age", "id": 3 }
ПОСЛЕ:
{ "name": "id", "id": 1 },{ "name": "person_name", "id": 2 },{ "name": "age", "id": 3 }
delete column
При удалении колонки Iceberg просто перестаёт читать данные, относящиеся к ней.
reorder columns
просто меняется позиция в списке
Пример: Физически parquet может хранить: [id][name][age] schema v1
|
position |
name |
id |
|---|---|---|
|
1 |
id |
1 |
|
2 |
name |
2 |
|
3 |
age |
3 |
|
меняем местами |
|
|
schema v2
|
position |
name |
id |
|---|---|---|
|
3 |
age |
3 |
|
1 |
id |
1 |
|
2 |
name |
2 |
partition evolution
Так же, как и со схемами, всё завязано не на переписывании самих данных, а на изменении metadata.
Пример:
у нас было партиционирование по дням, а мы хотим делать партиции по месяцу. Было:
{ "spec-id": 1, "fields": [ { "source-id": 2, "transform": "day" } ] }
Стало:
{ "spec-id": 2, "fields": [ { "source-id": 2, "transform": "month" } ] }
И получается, что старые и новые данные физически разбиты по-разному, но для пользователя это остаётся одной таблицей.
Заключение
Вся философия и весь принцип работы Iceberg заключается в metadata.
Благодаря ей мы можем эффективно выполнять запросы, получать статистику по колонкам, не тратя огромные ресурсы каждый раз, использовать time travel и branch для тестирования фичей и командной работы, изменять схемы и партиции, не переписывая сами файлы с данными.
ссылка на оригинал статьи https://habr.com/ru/articles/1033546/