Learning Platform
Глоссарий Troubleshooting
Урок 11.04 · 22 мин
Средний
delta-lakeoptimizevacuumregister-table

Delta-процедуры: OPTIMIZE, VACUUM, register_table

Delta-таблица, как и Iceberg, — живой объект: она деградирует и требует обслуживания. Проблемы те же — мелкие файлы и накопленная история, — но процедуры Delta называются иначе и в деталях работают по-своему. Этот урок разбирает три ключевые процедуры: OPTIMIZE для компакции, VACUUM для уборки старых файлов и register_table для подключения существующих Delta-таблиц. Опираясь на модуль по Iceberg, мы будем постоянно сравнивать — это лучший способ закрепить, что разные форматы решают одни задачи.

Почему Delta-таблица деградирует

Деградация Delta вытекает из тех же свойств, что у Iceberg, через призму transaction log из прошлого урока.

Мелкие файлы. Каждый INSERT добавляет новые Parquet-файлы и пишет в transaction log действие add. Дописать в существующий файл нельзя — файлы данных неизменяемы. Частые мелкие INSERT и стриминг плодят тысячи маленьких файлов. На чтении это бьёт так же, как у Iceberg: больше файлов — больше сплитов, больше накладных расходов на открытие файлов и чтение Parquet-footer.

Накопление мёртвых файлов. Когда UPDATE, DELETE или OPTIMIZE переписывают файл, в transaction log пишется remove для старого файла и add для нового. Но remove в журнале не означает физического удаления файла с диска — он лишь помечает файл как не входящий в текущее состояние таблицы. Старый Parquet-файл остаётся в object storage. Зачем? Ровно для time travel: запрос FOR VERSION AS OF к прошлой версии должен прочитать файлы, которые тогда были живыми. Пока эти версии достижимы, физически удалять старые файлы нельзя.

Итог: со временем в директории Delta-таблицы скапливаются и мелкие файлы (вредят чтению), и мёртвые файлы — те, что помечены remove и нужны только истории (вредят счёту за storage). Две оси деградации, как у Iceberg.

Деградация Delta-таблицы и процедуры обслуживания
Мелкие файлыЧастые INSERT и стриминг плодят маленькие Parquet-файлы; растёт число сплитов и накладные расходы на чтение
лечит OPTIMIZE
Мёртвые файлыФайлы, помеченные remove в transaction log; физически остаются на диске ради time travel и занимают место
лечит VACUUM
Здоровая таблицаКрупные файлы и убранные мёртвые файлы — быстрые запросы и контролируемый счёт за хранение

OPTIMIZE: компакция мелких файлов

OPTIMIZE в Delta решает проблему мелких файлов — ровно как одноимённая операция в Iceberg. Процедура читает мелкие файлы данных и переписывает их содержимое в меньшее число крупных файлов. В transaction log это фиксируется как новая транзакция: remove для мелких файлов, add для крупных. Данные логически не меняются — те же строки, эффективно упакованные.

В Trino компакция Delta вызывается через ALTER TABLE ... EXECUTE optimize:

-- Скомпактить мелкие файлы Delta-таблицы в крупные
ALTER TABLE delta.ops.accounts EXECUTE optimize;

-- Компактить только файлы меньше заданного размера
ALTER TABLE delta.ops.accounts
  EXECUTE optimize(file_size_threshold => '128MB');

Как и у Iceberg, на больших таблицах разумно ограничивать область компакции — деградирует обычно свежая партиция, в которую льются данные. OPTIMIZE принимает WHERE по партициям:

-- Компактить только вчерашнюю партицию
ALTER TABLE delta.ops.events
  EXECUTE optimize
  WHERE event_date = DATE '2026-05-19';

Ключевое, что нужно перенести из модуля по Iceberg: OPTIMIZE не освобождает место. Он создал новые крупные файлы, а мелкие пометил remove в журнале — но физически они остались в object storage, потому что нужны для time travel к прошлым версиям. После OPTIMIZE директория таблицы временно даже выросла. Чтение ускорилось, место не освободилось. Удалить мёртвые файлы — задача VACUUM.

VACUUM: физическое удаление мёртвых файлов

VACUUM физически удаляет из object storage файлы, которые помечены remove в transaction log и больше не нужны. Это аналог expire_snapshots из мира Iceberg — процедура, реально освобождающая место.

-- Удалить мёртвые файлы старше retention по умолчанию
ALTER TABLE delta.ops.accounts EXECUTE vacuum;

-- Явно задать порог retention
ALTER TABLE delta.ops.accounts
  EXECUTE vacuum(retention => '7d');

Здесь — критически важный момент, прямая параллель с минимальным retention у expire_snapshots. У VACUUM есть retention-порог (по умолчанию 7 дней), и понижать его опасно. Причина двойная.

Первая: VACUUM физически уничтожает файлы. Удалив мёртвый файл, вы навсегда теряете возможность time travel к версиям, которые на него ссылались. После VACUUM с порогом 7 дней time travel дальше 7 дней назад перестаёт работать — соответствующих файлов больше нет. Retention VACUUM — это и есть фактическая глубина вашего time travel.

Вторая, ещё опаснее: мёртвый файл и файл недавней или идущей прямо сейчас транзакции по виду в директории неотличимы. Слишком короткий retention рискует удалить файл операции, которая ещё не успела завершиться или на которую опирается параллельный читатель. Поэтому Delta защищает свежие файлы порогом и предупреждает при попытке его занизить.

DANGER

VACUUM необратим: удалённые файлы данных не восстановить. Перед запуском VACUUM с уменьшенным retention убедитесь, что более глубокий time travel вам действительно не нужен — после удаления файлов вернуть прошлые версии будет неоткуда. Retention VACUUM напрямую задаёт, насколько далеко в прошлое работает FOR VERSION AS OF и FOR TIMESTAMP AS OF.

Порядок процедур такой же, как у Iceberg: сначала OPTIMIZE (компакция, старые файлы становятся мёртвыми), затем VACUUM (физическая уборка мёртвых файлов). OPTIMIZE без последующего VACUUM ускоряет чтение, но не уменьшает счёт за storage.

OPTIMIZE и VACUUM: порядок и зона ответственности
OPTIMIZEКомпактит мелкие файлы в крупные; старые файлы помечаются remove в transaction log, но физически остаются — место не освобождается
затем
VACUUMФизически удаляет мёртвые файлы старше retention; освобождает место, но необратимо обрезает глубину time travel

Сравнение терминов с Iceberg, чтобы не путать:

ЗадачаIcebergDelta Lake
Компакция мелких файловoptimizeoptimize
Физическое удаление старого / освобождение местаexpire_snapshotsvacuum
Уборка файлов-сирот вне метаданныхremove_orphan_files(частично покрывается vacuum)
Что задаёт глубину time travelretention expire_snapshotsretention vacuum

register_table: подключение существующей Delta-таблицы

Последняя процедура — register_table. Она решает не деградацию, а онбординг: как сделать видимой в Trino Delta-таблицу, которая уже существует в object storage, но не зарегистрирована в metastore вашего каталога.

Сценарий типичный. Spark-пайплайн или платформа Databricks создали Delta-таблицу: в object storage есть директория данных и _delta_log/. Сама таблица полностью самодостаточна — transaction log внутри неё описывает всё. Но metastore вашего каталога Trino про эту таблицу не знает: для него нет записи «имя -> директория». register_table создаёт эту запись.

-- Зарегистрировать существующую Delta-таблицу в каталоге Trino.
-- Требует delta.register-table-procedure.enabled=true в каталоге.
CALL delta.system.register_table(
  schema_name => 'ops',
  table_name  => 'accounts_external',
  table_location => 's3://lake/external/accounts'
);

Процедура не трогает данные и не трогает _delta_log/ — она лишь добавляет в metastore указатель на директорию. После этого SELECT FROM delta.ops.accounts_external работает: коннектор по указателю находит директорию, читает _delta_log/ и видит таблицу целиком. Парная процедура unregister_table убирает указатель из metastore, тоже не удаляя данные.

Важная деталь безопасности: register_table по умолчанию выключена и требует явного delta.register-table-procedure.enabled=true в файле каталога. Причина — регистрация по произвольному пути потенциально опасна, и администратор должен включить эту возможность сознательно.

TIP

register_table — основной инструмент миграции на Trino без копирования данных. Если организация переходит с Spark-only на Trino как движок запросов, существующие Delta-таблицы не нужно переливать: достаточно зарегистрировать их по их путям. Данные остаются на месте, Trino начинает их читать. Это прямая выгода открытого формата таблиц — таблица не привязана к движку.

Delta Lake: checkpoints и компакция на уровне формата

Delta-обслуживание глазами transaction log

Свяжем процедуры обслуживания с моделью из прошлого урока — это даёт более глубокое понимание, чем заучивание имён процедур.

Вспомните: состояние Delta-таблицы — это свёртка transaction log. Каждый OPTIMIZE, DELETE, VACUUM — это новая транзакция, новый пронумерованный JSON-файл в _delta_log/. Отсюда первое наблюдение: обслуживание само по себе удлиняет журнал. OPTIMIZE не «убирает записи из журнала», он дописывает в него транзакцию с действиями remove и add. Журнал монотонно растёт.

Это объясняет роль checkpoint. Если бы журнал только рос, чтение состояния со временем требовало бы проигрывать всё больше JSON-файлов. Checkpoint — Parquet-файл со свёрнутым состоянием — периодически создаётся, чтобы движок доигрывал лишь хвост журнала после последнего checkpoint. Checkpoint к обслуживанию данных прямого отношения не имеет, но это часть «гигиены» Delta-таблицы: он держит стоимость чтения метаданных ограниченной независимо от того, сколько транзакций накопилось.

И ещё одно следствие. Когда VACUUM физически удаляет мёртвый файл данных, запись remove об этом файле в старых транзакциях журнала остаётся — журнал неизменяем, в нём нельзя стереть прошлое. Просто time travel к версии, которая ссылалась на удалённый файл, перестаёт работать: журнал помнит, что файл был, но самого файла на диске уже нет. Это согласуется с тем, что VACUUM необратим и его retention задаёт фактическую глубину time travel. Понимание через transaction log делает поведение процедур не набором правил, а следствием одной модели.

Попробуй сам

В песочнице с каталогом delta создайте таблицу events. Упражнение первое: сымитируйте стриминг — выполните 30-40 мелких INSERT, снимите метрику числа файлов (посмотрите директорию таблицы в MinIO или метаданные). Выполните ALTER TABLE EXECUTE optimize, снова посмотрите директорию — обратите внимание, что число файлов в _delta_log выросло, а физических Parquet-файлов стало не меньше, а больше. Объясните, почему OPTIMIZE не уменьшил число файлов на диске. Упражнение второе: выполните ALTER TABLE EXECUTE vacuum и снова посмотрите директорию — теперь мёртвые файлы убраны. Письменно ответьте: почему важен порядок OPTIMIZE затем VACUUM, и как retention у VACUUM связан с тем, насколько далеко назад работает time travel. Упражнение третье на register_table: возьмите путь существующей Delta-таблицы (или создайте таблицу, затем мысленно «забудьте» её регистрацию) и зарегистрируйте её под новым именем через register_table; убедитесь, что SELECT работает, и объясните, почему процедура не копировала данные.


Проверка знанийKnowledge check
Чем отличаются процедуры OPTIMIZE и VACUUM в Delta Lake, почему важен их порядок, и что делает register_table?
ОтветAnswer
OPTIMIZE и VACUUM лечат две разные оси деградации Delta-таблицы. OPTIMIZE решает проблему мелких файлов: частые INSERT и стриминг плодят маленькие Parquet-файлы, потому что файлы данных неизменяемы и дописать в них нельзя. OPTIMIZE читает мелкие файлы и переписывает их в меньшее число крупных, фиксируя в transaction log remove для мелких и add для крупных. Но OPTIMIZE не освобождает место: помеченные remove файлы физически остаются в object storage, потому что нужны для time travel к прошлым версиям — после OPTIMIZE директория даже временно растёт. VACUUM решает проблему мёртвых файлов: он физически удаляет из object storage файлы, помеченные remove и больше не нужные. Это аналог expire_snapshots в Iceberg — процедура, реально освобождающая место. Порядок важен потому, что OPTIMIZE создаёт мёртвые файлы (помечает мелкие remove), а освобождает место только последующий VACUUM; OPTIMIZE без VACUUM ускоряет чтение, но не уменьшает счёт за storage. У VACUUM есть retention-порог, и понижать его опасно: VACUUM необратим — удалив мёртвый файл, вы навсегда теряете time travel к версиям, что на него ссылались, поэтому retention VACUUM фактически задаёт глубину time travel; кроме того, мёртвый файл и файл идущей транзакции по виду неотличимы, и короткий retention рискует удалить данные незавершённой операции. register_table решает не деградацию, а онбординг: она регистрирует в metastore каталога Trino уже существующую в object storage Delta-таблицу, добавляя указатель имя -> директория, не трогая ни данные, ни transaction log. Это основной инструмент миграции на Trino без копирования данных; процедура по умолчанию выключена и требует явного включения в каталоге по соображениям безопасности.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что делает ALTER TABLE EXECUTE optimize для Delta-таблицы и почему после него число файлов в object storage не уменьшается?

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

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

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

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