Перейти к содержанию
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 из 7