Learning Platform
Глоссарий Troubleshooting
Урок 06.04 · 12 мин
Средний
CompactionVacuumOPTIMIZESmall FilesData Maintenance

Compaction и vacuum: обслуживание данных

Зачем нужно обслуживание данных

Данные в озере данных (data lake) имеют тенденцию деградировать со временем:

  1. Накопление маленьких файлов — каждый streaming micro-batch, каждая инкрементальная загрузка создаёт новые файлы
  2. Устаревшие версии — в Delta Lake/Iceberg каждая операция (INSERT, UPDATE, DELETE) создаёт новую версию данных, но старые файлы остаются
  3. Фрагментация данных — после множества UPDATE/DELETE данные разбросаны по файлам, data skipping теряет эффективность

Без обслуживания таблица из 100 оптимальных файлов превращается в 100,000 маленьких за несколько месяцев.

Compaction (OPTIMIZE)

Compaction — это процесс объединения множества маленьких файлов в меньшее количество крупных. В Delta Lake это делается командой OPTIMIZE:

-- Компактизация таблицы
OPTIMIZE events

-- Компактизация конкретной партиции
OPTIMIZE events
WHERE event_date = '2024-02-01'

-- Компактизация с Z-ordering
OPTIMIZE events
ZORDER BY (user_id, event_type)
# Через DeltaTable API
from delta.tables import DeltaTable

delta_table = DeltaTable.forPath(spark, "/data/events/")
delta_table.optimize().executeCompaction()

# С Z-ordering
delta_table.optimize().executeZOrderBy("user_id", "event_type")

Что происходит при OPTIMIZE

OPTIMIZE: Compaction
До OPTIMIZE (10,000 файлов по ~1MB)
├──part-00000.parquet(800KB)
├──part-00001.parquet(1.2MB)
├──part-00002.parquet(500KB)
├──... (9,997 файлов)
└──part-09999.parquet(900KB)
10,000 файлов × ~1MB = ~10GB

Целевой размер файла: по умолчанию spark.databricks.delta.optimize.maxFileSize = 1073741824 (1GB) в Databricks, или delta.targetFileSize = 134217728 (128MB) в open-source Delta Lake.

Bin-packing vs Z-ordering

OPTIMIZE может работать в двух режимах:

РежимЧто делаетКогда использовать
Bin-packing (default)Объединяет маленькие файлы, сохраняя порядокКогда data skipping уже эффективен
Z-orderingОбъединяет + перераспределяет по Z-кривойКогда нужен data skipping по нескольким столбцам

Z-ordering при OPTIMIZE — единственный способ применить Z-ordering к существующим данным.

Vacuum: очистка устаревших файлов

После OPTIMIZE старые маленькие файлы не удаляются — они остаются для поддержки time travel (запросы к предыдущим версиям данных). VACUUM удаляет файлы, которые старше retention period:

-- Удалить файлы старше 7 дней (default)
VACUUM events

-- Удалить файлы старше 30 дней
VACUUM events RETAIN 720 HOURS
# Через DeltaTable API
delta_table = DeltaTable.forPath(spark, "/data/events/")
delta_table.vacuum(retentionHours=168)  # 7 дней

Что удаляет VACUUM

VACUUM RETAIN 168 HOURS (7 дней)
├──part-00000.parquet98MBСохранён (текущий)
├──part-00001.parquet102MBСохранён (текущий)
├──_delta_log/Сохранён (metadata)
├──old-part-00000.parquet800KB(7+ дней)Удалён
├──old-part-00001.parquet1.2MB(7+ дней)Удалён
├──old-part-00002.parquet500KB(3 дня)Сохранён (< 7 дней)
└──old-part-00003.parquet900KB(7+ дней)Удалён

3 файла удалены — старше 7 дней retention period

Конфигурация retention

ПараметрDefaultОписание
delta.deletedFileRetentionDurationinterval 7 daysМинимальный retention для VACUUM
delta.logRetentionDurationinterval 30 daysRetention для _delta_log
spark.databricks.delta.retentionDurationCheck.enabledtrueЗащита от слишком маленького retention
# Установка retention для таблицы
spark.sql("""
  ALTER TABLE delta.`/data/events/`
  SET TBLPROPERTIES (
    'delta.deletedFileRetentionDuration' = 'interval 30 days'
  )
""")
WARNING

VACUUM с retention < 7 дней опасен! Time travel позволяет запрашивать предыдущие версии таблицы. Если VACUUM удалит файлы, от которых зависит активный запрос или конкурентная запись, результат — ошибка FileNotFoundException или corrupted data. По умолчанию Spark блокирует VACUUM с retention < 7 дней.

Анти-паттерн: VACUUM с retention < 7 дней

# ПЛОХО: агрессивный vacuum
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "false")
delta_table.vacuum(retentionHours=0)  # удалить ВСЁ старое

# Последствия:
# 1. Time travel сломан -- нельзя запросить вчерашнюю версию
# 2. Конкурентные читатели могут получить FileNotFoundException
# 3. Streaming checkpoint может указывать на удалённые файлы
# ХОРОШО: разумный retention
delta_table.vacuum(retentionHours=168)  # 7 дней (default)

# Для critical tables с длительными jobs:
delta_table.vacuum(retentionHours=720)  # 30 дней

Планирование maintenance jobs

Compaction и vacuum должны выполняться регулярно как maintenance jobs:

# Ежедневный maintenance job
from datetime import datetime, timedelta

def daily_maintenance(table_path: str):
    delta_table = DeltaTable.forPath(spark, table_path)

    # 1. Компактизация файлов
    delta_table.optimize().executeCompaction()

    # 2. Очистка устаревших версий
    delta_table.vacuum(retentionHours=168)

    print(f"Maintenance complete for {table_path} at {datetime.now()}")

# Расписание: ежедневно в 03:00 (off-peak hours)
tables = ["/data/events/", "/data/orders/", "/data/users/"]
for table in tables:
    daily_maintenance(table)

Рекомендации по расписанию

Частота записиOPTIMIZEVACUUM
Streaming (continuous)Каждые 1-4 часаЕжедневно
Micro-batch (hourly)ЕжедневноЕженедельно
Daily appendЕженедельноЕжемесячно
Редкие обновленияПо необходимостиЕжемесячно
TIP

Подробнее о Delta Lake, Iceberg и Hudi — в модуле Lakehouse (Phase 66). Там мы разберём ACID-транзакции, time travel, schema evolution и production-ready конфигурации.

Проверка знанийKnowledge check
Почему OPTIMIZE не удаляет старые файлы автоматически после создания новых компактных файлов?
ОтветAnswer
OPTIMIZE создаёт новые крупные файлы, но сохраняет старые для поддержки time travel в Delta Lake. Time travel позволяет запрашивать данные на момент прошлых версий таблицы (SELECT * FROM table VERSION AS OF 5). Если удалить старые файлы сразу, time travel сломается. Кроме того, конкурентные читатели могут читать старые файлы в момент OPTIMIZE. Для удаления используется отдельная команда VACUUM с настраиваемым retention period.
Проверка знанийKnowledge check
Production-таблица обновляется каждый час streaming-job'ом. Как настроить maintenance для оптимальной производительности?
ОтветAnswer
1) OPTIMIZE каждые 2-4 часа (не каждый час -- OPTIMIZE сам создаёт load). Можно настроить по количеству новых файлов: если накопилось > 100 мелких файлов, запускать OPTIMIZE. 2) VACUUM ежедневно с retention = 7 дней (default). 3) Запускать maintenance в off-peak часы (ночью). 4) OPTIMIZE с ZORDER BY для столбцов, часто используемых в WHERE. 5) Мониторить число файлов per partition: если > 500 -- OPTIMIZE нужен срочно.
TIP

Для углублённого изучения maintenance-операций lakehouse-форматов см. курс Storage Formats Deep-Dive — compaction и vacuum в Delta Lake, и maintenance в Iceberg (expire snapshots, rewrite data files).

Что дальше?

В следующем уроке мы подробно разберём проблему маленьких файлов — почему она возникает, как диагностировать и какие инструменты Spark предоставляет для её решения помимо OPTIMIZE.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Зачем нужна compaction (OPTIMIZE) в Delta Lake/Iceberg?

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

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

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

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