Learning Platform
Глоссарий Troubleshooting
Урок 10.03 · 24 мин
Средний
icebergsnapshotsmanifestsmetadata

Iceberg connector: каталоги, метаданные, snapshots

Мы выяснили: lakehouse-таблица — это файлы данных плюс слой метаданных, а каталог хранит указатель на текущую версию метаданных. Теперь заглянем внутрь самого слоя метаданных. Что конкретно лежит в metadata-файле? Что такое snapshot и manifest? И как координатор Trino, прочитав это дерево, понимает, какие именно Parquet-файлы и в каком порядке отдать воркерам? Без этой картины невозможно осмысленно делать ни time travel, ни обслуживание таблиц, ни pruning — поэтому этот урок разбирает анатомию Iceberg-таблицы по слоям.

Трёхуровневое дерево метаданных

Метаданные Iceberg-таблицы — это не один файл, а дерево из трёх уровней. Каждый уровень — отдельные файлы в том же object storage, что и данные. Идти по дереву нужно сверху вниз, и Trino именно так его и читает.

Уровень 1: metadata-файл. На него указывает каталог. Это JSON-файл (vNNN.metadata.json). Он хранит описание таблицы целиком: текущую схему (и историю прошлых схем), спецификацию партиционирования, свойства таблицы и — главное — список всех snapshot’ов и указание, какой из них текущий. Каждое изменение таблицы создаёт новый metadata-файл с новым набором снапшотов.

Уровень 2: snapshot и manifest list. Snapshot — это состояние таблицы на один момент времени, то есть полный набор файлов данных в этот момент. Сам snapshot не перечисляет файлы данных напрямую — он указывает на manifest list: один файl (в формате Avro), который перечисляет манифесты этого снапшота. Manifest list для каждого манифеста хранит сводную статистику — диапазоны значений партиционирующих колонок. Это позволяет на уровне списка отбросить целые манифесты, не открывая их.

Уровень 3: manifest. Manifest — тоже Avro-файл. Он перечисляет конкретные файлы данных (Parquet/ORC): для каждого файла — путь, число строк, размер, к какой партиции относится и, критически важно, статистику по колонкам: min/max значений, доля null, количество значений. Manifest также помечает каждый файл как добавленный, существующий или удалённый — это нужно для инкрементального чтения и для delete-файлов.

Трёхуровневое дерево метаданных Iceberg
metadata-файл (JSON)vNNN.metadata.json — схема, партиционирование, свойства, список всех снапшотов и указание текущего; на него указывает каталог
текущий снапшот указывает на manifest list
manifest list (Avro)Один файл на снапшот; перечисляет манифесты и хранит диапазоны партиционирующих колонок для каждого — позволяет отбросить целые манифесты
каждый манифест перечисляет файлы данных
manifest (Avro)Перечисляет файлы данных: путь, число строк, партиция и статистика по колонкам min/max/nulls — основа для file pruning
манифест ссылается на
файлы данных (Parquet)Собственно строки таблицы, колоночно сжатые в Parquet или ORC в object storage

Зачем три уровня, а не один список файлов? Потому что у зрелой таблицы миллионы файлов данных. Хранить их одним списком — значит читать гигабайты метаданных на каждый запрос. Дерево позволяет читать метаданные выборочно: по manifest list отбросить манифесты ненужных партиций, не открывая их; внутри оставшихся манифестов по статистике отбросить ненужные файлы данных. Метаданные читаются почти так же избирательно, как данные. Это и есть инженерное ядро Iceberg.

Iceberg: снапшоты, коммиты и time travel Iceberg: hidden partitioning и partition evolution

Snapshot — это immutable срез таблицы

Ключевое свойство снапшота — он неизменяемый. Раз созданный, snapshot никогда не меняется: он навсегда описывает один набор файлов. Изменение таблицы не правит существующий snapshot — оно создаёт новый.

Разберём, что происходит при INSERT. Воркеры пишут новые Parquet-файлы. Создаётся новый манифест, перечисляющий их. Создаётся новый manifest list — он включает и новый манифест, и манифесты из предыдущего снапшота (старые файлы никуда не делись). Создаётся новый snapshot, указывающий на этот manifest list. Создаётся новый metadata-файл, добавляющий этот snapshot в список и помечающий его текущим. Финал — каталог атомарно переключает указатель.

Предыдущий snapshot при этом остаётся жив и валиден. Все его файлы данных на месте, его manifest list и манифесты на месте. Запрос, начавшийся до коммита, продолжает читать старый snapshot и видит согласованные данные. Это даёт три вещи сразу: атомарность (новый snapshot виден весь или никак), snapshot isolation для конкурентных запросов и — бонусом — time travel: раз старые снапшоты не удаляются, к ним можно обратиться явно. Этому посвящён следующий урок.

NOTE

Старые снапшоты живут не вечно — иначе таблица копила бы все файлы за всю историю. Их удаляет процедура expire_snapshots из урока про обслуживание таблиц. До тех пор пока снапшот не expired, его данные доступны для чтения и time travel. Это сознательный компромисс: история занимает место в object storage, и инженер сам решает, сколько её хранить.

Как Trino читает Iceberg-таблицу

Соберём всё в сквозной путь запроса SELECT ... FROM iceberg.sales.orders WHERE order_date = DATE '2026-05-01'.

Сначала координатор спрашивает каталог: где текущий metadata-файл sales.orders? Получает путь, читает JSON, находит текущий snapshot. Из снапшота берёт manifest list и читает его. Здесь срабатывает первый отсев: manifest list хранит диапазоны партиционирующих колонок по каждому манифесту — манифесты, чьи диапазоны заведомо не пересекаются с order_date = '2026-05-01', отбрасываются целиком, не открываясь.

Затем координатор читает оставшиеся манифесты. В них — список файлов данных со статистикой min/max по колонкам. Здесь второй отсев: файл, у которого min/max order_date не покрывает '2026-05-01', пропускается. Это и есть file pruning на основе статистики из манифестов. По выжившим файлам координатор генерирует сплиты — обычно сплит соответствует файлу или его части (row group). Воркеры читают эти Parquet-файлы из object storage параллельно.

Путь запроса Trino через метаданные Iceberg
КаталогВозвращает путь к текущему metadata-файлу таблицы
metadata-файлКоординатор находит текущий snapshot
manifest listПервый отсев: отбрасываются манифесты с непересекающимися диапазонами партиций
manifestВторой отсев: file pruning по статистике min/max колонок в манифесте
сплиты воркерамПо выжившим файлам генерируются сплиты, воркеры читают Parquet параллельно

Главный вывод: чем больше отсеялось на уровне метаданных, тем меньше Parquet Trino реально откроет. Поэтому статистика в манифестах — не «приятное дополнение», а механизм, напрямую определяющий объём I/O. Партиционирование, которое мы разберём в уроке 6, работает именно через эти диапазоны в manifest list.

Настройка коннектора и проверка на практике

Файл каталога Trino для Iceberg задаёт коннектор, тип каталога метаданных и доступ к object storage.

# etc/catalog/iceberg.properties
connector.name=iceberg
iceberg.catalog.type=rest
iceberg.rest-catalog.uri=http://rest-catalog:8181
iceberg.rest-catalog.warehouse=s3://lake/warehouse
fs.native-s3.enabled=true
s3.endpoint=http://minio:9000
s3.path-style-access=true
# Формат файлов по умолчанию для новых таблиц
iceberg.file-format=PARQUET

Создадим таблицу и сделаем два INSERT — каждый создаёт snapshot:

CREATE TABLE iceberg.sales.orders (
  order_id   BIGINT,
  order_date DATE,
  amount     DECIMAL(12,2)
) WITH (format = 'PARQUET');

INSERT INTO iceberg.sales.orders VALUES
  (1, DATE '2026-05-01', DECIMAL '250.00'),
  (2, DATE '2026-05-01', DECIMAL '99.90');
-- INSERT: 2 rows

INSERT INTO iceberg.sales.orders VALUES
  (3, DATE '2026-05-02', DECIMAL '12.50');
-- INSERT: 1 row

Дерево метаданных видно через системные metadata-таблицы — суффикс $ к имени таблицы. Полный их разбор — в уроке 7, но snapshots посмотрим уже сейчас, два INSERT дали два снапшота:

SELECT snapshot_id, operation,
       summary['added-records'] AS added_records
FROM iceberg.sales."orders$snapshots"
ORDER BY committed_at;

--    snapshot_id     | operation | added_records
-- -------------------+-----------+---------------
--  6841...300912934  | append    | 2
--  9920...118574521  | append    | 1

Таблица $files показывает уровень 3 дерева — конкретные файлы данных и их статистику, ту самую, по которой идёт pruning:

SELECT file_path, record_count, file_size_in_bytes
FROM iceberg.sales."orders$files";

--                file_path                 | record_count | file_size_in_bytes
-- -----------------------------------------+--------------+--------------------
--  s3://lake/.../data/00000-...-abc.parquet |            2 |               714
--  s3://lake/.../data/00000-...-def.parquet |            1 |               655

Два файла данных — по одному на INSERT. Каждый INSERT не дописывает в существующий файл, а создаёт новый: файлы данных в Iceberg тоже неизменяемы. Это объясняет, почему частые мелкие INSERT плодят мелкие файлы и зачем нужна компакция (OPTIMIZE) — тема урока 5.

Почему дерево метаданных — это не лишний слой

У начинающих возникает разумное возражение: трёхуровневое дерево метаданных — это же дополнительная работа, не замедляет ли оно запросы по сравнению с «просто папкой файлов»? Ответ — нет, и понимать почему важно.

Сравним стоимость. У data lake без формата таблиц «определение списка файлов» — это операция list по директории. На таблице с миллионом файлов list возвращает миллион записей, и это всё, что есть, — никакой статистики, чтобы их отсеять. Дальше движок вынужден открывать файлы и читать их footer, чтобы понять, нужны ли они. У Iceberg «определение списка файлов» — это чтение дерева метаданных, и оно устроено так, чтобы прочитать меньше: manifest list компактен и сразу отсекает целые манифесты, а в манифесте статистика лежит рядом со списком файлов, так что отсев файла не требует открывать сам файл. Iceberg добавляет слой метаданных, но этот слой спроектирован так, чтобы суммарно читать меньше, а не больше.

Есть и второй выигрыш — точность. Координатор Trino, прочитав манифесты, знает точное число файлов, их размеры и статистику ещё до генерации сплитов. Это даёт ему основу и для аккуратного планирования (сколько сплитов создавать, как их распределить), и для cost-based optimizer (число строк, min/max — это и есть статистика для оценки кардинальности). У «папки файлов» этой информации нет заранее, поэтому и планировать, и оптимизировать запрос движок вынужден почти вслепую. Дерево метаданных — это не накладной расход, а то, что одновременно ускоряет чтение и делает возможной осмысленную оптимизацию.

Попробуй сам

Поднимите песочницу курса (Trino + REST catalog + MinIO) и воспроизведите пример: создайте iceberg.sales.orders, сделайте три отдельных INSERT. Затем исследуйте дерево метаданных. Посмотрите orders$snapshots — сколько снапшотов, какой operation у каждого. Посмотрите orders$files — сколько файлов данных и почему столько. Посмотрите orders$manifests — сколько манифестов. Теперь главный вопрос на понимание: выполните SELECT * FROM orders$files и найдите в выводе колонки со статистикой по колонкам данных (нижние и верхние границы значений). Объясните своими словами, как координатор Trino использовал бы эти границы, если бы вы выполнили SELECT * FROM orders WHERE order_date = DATE '2026-05-02' — какие файлы он открыл бы, а какие пропустил и на основании чего.


Проверка знанийKnowledge check
Опишите трёхуровневое дерево метаданных Iceberg-таблицы и объясните, зачем нужны именно три уровня, а не один плоский список файлов данных.
ОтветAnswer
Дерево метаданных Iceberg состоит из трёх уровней файлов в object storage. Верхний уровень — metadata-файл в формате JSON, на который указывает каталог; он хранит схему, спецификацию партиционирования, свойства и список всех снапшотов с указанием текущего. Средний уровень — snapshot, состояние таблицы на момент времени; снапшот указывает на manifest list — Avro-файл, перечисляющий манифесты этого снапшота и хранящий для каждого диапазоны партиционирующих колонок. Нижний уровень — manifest, тоже Avro; он перечисляет конкретные файлы данных и для каждого хранит путь, число строк, партицию и статистику по колонкам min/max/nulls. Манифесты ссылаются на сами файлы данных в Parquet или ORC. Три уровня нужны потому, что у зрелой таблицы миллионы файлов данных, и хранить их одним плоским списком означало бы читать гигабайты метаданных на каждый запрос. Дерево позволяет читать метаданные выборочно: по диапазонам в manifest list отбрасываются целые манифесты ненужных партиций без их открытия, а внутри оставшихся манифестов по статистике min/max отбрасываются ненужные файлы данных. В итоге метаданные читаются почти так же избирательно, как данные, и чем больше отсеялось на уровне метаданных, тем меньше Parquet-файлов Trino реально откроет.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. В каком порядке расположены три уровня дерева метаданных Iceberg, начиная от того, на который указывает каталог?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7