1. Архитектура и метаданные Apache Iceberg: слои catalog, metadata file, snapshots, manifest list, manifest files и data files
Архитектура и метаданные Apache Iceberg: слои catalog, metadata file, snapshots, manifest list, manifest files и data files
Представьте, что вы открыли директорию на S3 с 50 000 Parquet-файлов и вам нужно найти все записи за вчерашний день. В классическом Hive вы бы запустили list-операцию по тысячам каталогов, потратив минуты на планирование запроса ещё до чтения первого байта данных. Apache Iceberg решает эту проблему архитектурно: он вводит многоуровневую иерархию метаданных, которая позволяет движку определить нужные файлы за миллисекунды, не перечисляя каталоги вообще.
Почему метаданные — это сердце Iceberg
Iceberg — это не сервис и не база данных. Это открытая спецификация табличного формата плюс набор библиотек, которые говорят движку (Spark, Trino, Flink), как правильно читать и писать данные поверх объектного хранилища. Вся магия — в том, что между движком и файлами данных располагается структурированный слой метаданных. Именно он обеспечивает ACID, time travel и мгновенный pruning ненужных файлов.
> Iceberg — это прослойка между движком и данными. По факту это просто библиотека о том, как хранить данные. Не более. Не сервис. Не СУБД. Просто набор библиотек для каждого движка, который записывает данные. > > habr.com
Шесть слоёв архитектуры
Архитектура Iceberg организована как иерархия из шести уровней. Двигаясь от верха к низу, каждый уровень ссылается на следующий — от точки входа до сырых данных.
Catalog — точка входа
Catalog — это внешний сервис, который хранит отображение «имя таблицы → путь к текущему metadata file». Когда движок выполняет запрос к таблице analytics.events, он первым делом обращается именно к catalog.
Реализации catalog:
| Реализация | Хранение указателя | Когда использовать | |---|---|---| | Hive Metastore | В базе данных HMS | Существующая инфраструктура Hive | | AWS Glue | Managed-сервис AWS | Облачные деплойменты на AWS | | REST Catalog | Отдельный HTTP-сервис | Кроссплатформенные сценарии, Docker-развёртывания | | JDBC Catalog | PostgreSQL / MySQL | Простые on-premise стенды | | Hadoop Catalog | Файловая система (HDFS) | Тестовые среды, legacy |
Catalog не хранит данные — он хранит указатель. Это ключевое отличие от монолитных СУБД: catalog можно заменить, не трогая ни данные, ни метаданные.
Metadata file — паспорт таблицы
Metadata file (или просто metadata) — JSON-файл, содержащий:
Каждый metadata file — это полное описание таблицы на момент коммита. При каждом изменении таблицы (INSERT, DELETE, ALTER) создаётся новый metadata file, а catalog обновляет указатель. Старые metadata file не удаляются — они нужны для time travel.
Snapshot — снимок состояния таблицы
Snapshot — это неизменяемое состояние таблицы в конкретный момент времени. Каждый коммит порождает новый snapshot. Один snapshot ссылается на один manifest list.
Snapshot содержит:
> 1 manifest list — это 1 snapshot. Это важное сведение. Супер важное. > > habr.com
Snapshot — это единица, на которой строятся ACID-изоляция и time travel. Движок всегда читает конкретный snapshot, а не «текущее состояние» — именно поэтому запросы воспроизводимы.
Manifest list — оглавление снапшота
Manifest list — Avro-файл, содержащий список manifest files, входящих в данный snapshot. Помимо путей к manifest files, он хранит агрегированную статистику: диапазоны значений partition columns для каждого manifest file. Это позволяет движку пропустить целые manifest files ещё до их чтения — первый уровень pruning.
Manifest files — индекс файлов данных
Manifest file — Avro-файл, описывающий набор data files. Для каждого data file в manifest хранятся:
write.metadata.metrics.*)Именно manifest files позволяют реализовать file-level pruning: движок сравнивает min/max каждого файла с предикатом запроса и пропускает файлы, которые заведомо не содержат подходящих данных.
Data files — сырые данные
Data files — Parquet (или Avro/ORC) файлы с фактическими данными. Это единственный слой, который содержит строки таблицы. Iceberg не навязывает конкретный формат, но на практике Parquet де-факто стандарт благодаря колоночному хранению, встроенному сжатию и поддержке predicate pushdown.
Как работает чтение: путь запроса
Когда Trino выполняет SELECT * FROM events WHERE date = '2024-01-15', происходит следующее:
eventsdate = '2024-01-15'Двойной pruning (на уровне manifest list и manifest files) — это то, что делает Iceberg быстрым на таблицах с миллионами файлов. Движку не нужно перечислять каталоги: вся информация о файлах уже в метаданных.
Как работает запись: путь коммита
При INSERT INTO events VALUES (...) движок:
Шаг 5 — это и есть атомарный коммит. Если два писателя пытаются закоммитить одновременно, только один победит (optimistic concurrency), а второй получит конфликт и повторит попытку. Данные, записанные проигравшим, остаются в storage как orphan files — их позже подчистит maintenance.
Практический пример: что лежит на диске
После нескольких INSERT-операций в таблицу analytics.events в S3 вы увидите примерно такую структуру:
Каждый файл в директории metadata/ — это часть иерархии метаданных. Catalog хранит только одну строку: «таблица analytics.events → v3.metadata.json». Всё остальное — цепочка ссылок вниз к data files.
Неочевидные нюансы
Metadata file не удаляются автоматически. Старые версии накапливаются и нужны для time travel. Без периодической очистки (expire_snapshots) директория metadata будет расти.
Manifest files могут описывать data files из разных партиций. Один manifest file не обязательно содержит файлы только одной партиции — Iceberg группирует их для оптимизации, но это не жёсткое правило.
Catalog — это единственная точка согласованности. Если catalog недоступен, новые запросы не смогут определить актуальный snapshot. При этом чтение по уже известному snapshot ID продолжит работать — metadata file и data files лежат в object storage независимо.