Learning Platform
Глоссарий Troubleshooting
Урок 11.06 · 30 мин
Продвинутый
Delta LakeApache IcebergApache HudiApache PaimonTable Format EvolutionSchema EnforcementPartition EvolutionType Widening

Эволюция схем в Table Formats

В уроке 01 мы разобрали, что Parquet и ORC сами по себе поддерживают только базовую эволюцию: добавление колонок в конец. Переименование, удаление, изменение порядка — невозможны без перезаписи файлов.

Table formats (Delta Lake, Iceberg, Hudi, Paimon) решают эту проблему: они добавляют metadata layer поверх Parquet/ORC, который отслеживает колонки не по позиции, а по ID или имени, и позволяет менять схему без rewrite данных.

Каждый формат решает эту задачу по-разному. Iceberg — наиболее мощный (ID-based tracking, partition evolution). Delta Lake — фокус на enforcement и type widening. Hudi — минимальная эволюция. Paimon — streaming-first подход.

Delta Lake: Schema Enforcement + Evolution

Delta Lake различает два режима:

Schema Enforcement (по умолчанию)

Schema enforcement — это строгая проверка: каждая write-операция должна точно соответствовать текущей схеме таблицы. Если schema не совпадает — write отклоняется:

# Текущая схема: id: int, name: string, email: string
# Попытка записать: id: int, name: string, phone: string
# → AnalysisException: phone не существует в схеме таблицы
df_with_phone.write.format("delta").mode("append").save("/data/users")

Enforcement защищает от случайного добавления “мусорных” колонок. Это schema-on-write: ошибка обнаруживается в момент записи, а не при чтении.

Schema Evolution (opt-in)

Чтобы разрешить эволюцию, нужно явно включить mergeSchema:

# Добавление новой колонки phone
df_with_phone.write.format("delta") \
 .mode("append") \
 .option("mergeSchema", "true") \
 .save("/data/users")
Delta Lake: Schema Enforcement vs Evolution

Write: новая колонка

DataFrame содержит данные с новой колонкой (phone), которой нет в текущей схеме таблицы Delta

mergeSchema?

Delta проверяет: совпадает ли schema DataFrame со schema таблицы? Если нет — проверяет флаг mergeSchema.
false (default)

AnalysisException Нет

AnalysisException — write отклонён. Данные не записаны. Схема таблицы не изменена. Это schema enforcement — защита от случайных изменений.
true

Schema merge → commit

Delta обновляет schema в _delta_log — добавляет новую колонку. Новые Parquet файлы содержат колонку. Старые файлы — нет (при чтении вернут NULL).

Успех: schema обновлена +

Запись успешна. Новая колонка добавлена в metadata. При чтении старых файлов без этой колонки — значение NULL.

Поддерживаемые операции

ОперацияПоддержкаМеханизм
Добавление колонкиmergeSchema или ALTER TABLE ADD COLUMN
Удаление колонкиALTER TABLE DROP COLUMN (column mapping mode)
ПереименованиеALTER TABLE RENAME COLUMN (column mapping mode)
Изменение порядкаНе поддерживается
Type wideningbyte→short→int→long, float→double, date→timestamp
Изменение nullabilityNOT NULL → nullable (обратно — нельзя без rewrite)

Type Widening

Delta Lake поддерживает безопасное расширение типов без rewrite:

-- Включить type widening для таблицы
ALTER TABLE users SET TBLPROPERTIES ('delta.enableTypeWidening' = 'true');

-- Теперь можно менять тип
ALTER TABLE users ALTER COLUMN age TYPE BIGINT; -- int → bigint

Безопасные widening-пути: byte → short → int → long, float → double, date → timestampNTZ.

WARNING

Column mapping mode (delta.columnMapping.mode = 'name' или 'id') — обязательное условие для rename/drop операций. По умолчанию Delta использует позиционный маппинг (column 0 = первая колонка), что не позволяет переименовывать или удалять. Включение column mapping — одноразовая операция, обратно отключить нельзя.

Apache Iceberg: ID-based Column Tracking

Iceberg — наиболее мощный формат с точки зрения schema evolution. Ключевое отличие: каждая колонка имеет уникальный integer ID, который никогда не переиспользуется.

Как работает ID-based tracking

Iceberg: ID-based Column Tracking
Iceberg SchemaIceberg schema хранится в metadata JSON. Каждое поле имеет уникальный field-id (monotonic integer). ID присваивается при создании колонки и никогда не меняется.
field-id mapping
Parquet File (старый)Parquet файл, записанный до изменений. Содержит колонки с field-id 1, 2, 3. Iceberg читает его, маппя колонки по field-id, а не по позиции или имени.
Чтение: маппинг по field-id

Result: id=1 +, name=2 +, email=3 SKIP, phone=4 → NULL

При чтении Iceberg сопоставляет колонки Parquet файла с текущей схемой по field-id. Колонка с id=3 (email) удалена из schema → пропускается. Колонка id=4 (phone) — нет в файле → NULL.

Все операции Iceberg

Iceberg поддерживает полный набор schema evolution операций — без rewrite данных:

ОперацияПоддержкаКак работает
Добавление колонкиНовый field-id, старые файлы → NULL
Удаление колонкиField-id помечается deleted, при чтении — пропускается
ПереименованиеМеняется имя, field-id остаётся — маппинг не ломается
Изменение порядкаПорядок — metadata only, файлы не перезаписываются
Type wideningint→long, float→double, decimal precision increase
Вложенные структыПоля внутри struct/map/list тоже имеют field-id

Partition Evolution

Уникальная фича Iceberg — partition evolution: можно менять partition scheme без rewrite:

-- Начали с партиционирования по дням
ALTER TABLE events ADD PARTITION FIELD day(event_time);

-- Через год: данных слишком много, меняем на часы
ALTER TABLE events REPLACE PARTITION FIELD day(event_time) WITH hour(event_time);

Iceberg хранит partition spec per-snapshot. Старые файлы остаются в дневных партициях, новые — в часовых. При query planning Iceberg использует оба spec для partition pruning.

TIP

Partition evolution — одно из главных преимуществ Iceberg над Delta Lake. В Delta Lake изменение partitioning требует полной перезаписи таблицы (REPLACE TABLE). В Iceberg — одна metadata-операция.

Iceberg Spec v3

Spec v3 (стабилизирован в 2025) добавляет:

  • Row-level lineage: отслеживание происхождения строк
  • Multi-argument transforms: composite partition transforms
  • Default values: значения по умолчанию для новых колонок (ранее всегда NULL)
  • Variant type: semi-structured данные (JSON-like) с типизацией

Apache Hudi: Ограниченная эволюция

Hudi исторически фокусировался на upsert/incremental processing, а не на schema evolution. Возможности эволюции ограничены:

ОперацияПоддержкаКомментарий
Добавление колонкиПоддерживается, старые файлы → NULL
Удаление колонки!Через schema-on-read (колонка игнорируется), не из метаданных
ПереименованиеНе поддерживается нативно
Изменение порядкаНе поддерживается
Type widening!Ограниченный набор: int→long, float→double
NOTE

Hudi 1.x значительно улучшил ситуацию с metadata management, но schema evolution по-прежнему не является фокусом проекта. Если вам критичны rename/drop/reorder — Iceberg или Delta Lake будут лучшим выбором.

Apache Paimon: Streaming-first эволюция

Apache Paimon — table format, оптимизированный для streaming use cases (Flink-native).

Schema evolution в Paimon:

ОперацияПоддержкаМеханизм
Добавление колонкиALTER TABLE ADD COLUMN
Удаление колонкиALTER TABLE DROP COLUMN
ПереименованиеALTER TABLE RENAME COLUMN
Изменение типаОграниченный набор safe casts
Partition evolutionТребует recreate таблицы

Paimon использует schema ID для версионирования — каждый data file ссылается на конкретную версию schema. При чтении файлов с другой schema version — Paimon выполняет schema reconciliation (аналогично Avro ResolvingDecoder, но на уровне колоночных файлов).

Streaming-first особенность: Paimon поддерживает schema evolution в streaming write через Flink CDC:

-- Flink SQL: автоматическая синхронизация schema из MySQL CDC
CREATE TABLE paimon_users WITH (
 'merge-engine' = 'deduplicate'
) AS TABLE mysql_users;
-- Если в MySQL добавляется колонка — Paimon автоматически обновляет schema

Сравнительная матрица: Table Format Evolution

Сравнение schema evolution в table formats
ОперацияТип schema evolution операции
Delta LakeDelta Lake — Databricks, Spark-native, schema enforcement
IcebergApache Iceberg — Netflix, ID-based tracking, partition evolution
HudiApache Hudi — Uber, upsert-focused, ограниченная эволюция
PaimonApache Paimon — streaming-first, Flink-native
Add columnДобавление новой колонки без перезаписи существующих файлов
ДаmergeSchema или ALTER TABLE ADD COLUMN
ДаНовый field-id, старые файлы → NULL
ДаПоддерживается, старые файлы → NULL
ДаALTER TABLE ADD COLUMN
Drop columnУдаление колонки из схемы без перезаписи файлов
Да *Требует column mapping mode (name или id). Без него — не поддерживается.
ДаField-id помечается deleted. Данные в файлах остаются, но не читаются.
!Только schema-on-read: колонка не удаляется из метаданных, просто игнорируется при чтении.
ДаALTER TABLE DROP COLUMN
Rename columnПереименование колонки без перезаписи файлов
Да *Требует column mapping mode. Переименование — metadata-only операция.
ДаField-id не меняется, только имя в metadata. Маппинг по ID не ломается.
НетНе поддерживается нативно. Workaround: drop + add (теряет данные).
ДаALTER TABLE RENAME COLUMN
Reorder columnsИзменение порядка колонок без перезаписи файлов
НетНе поддерживается. Порядок определяется schema в _delta_log.
ДаПорядок — metadata only. Можно менять FIRST/AFTER.
НетНе поддерживается.
НетНе поддерживается нативно.
Type wideningРасширение типа колонки (int→long, float→double) без перезаписи
ДаenableTypeWidening. Пути: byte→short→int→long, float→double, date→timestamp.
Даint→long, float→double, decimal precision increase.
!Ограниченный набор: int→long, float→double.
ДаОграниченный набор safe casts.
Partition evolutionИзменение partition scheme без перезаписи данных
НетТребует REPLACE TABLE — полная перезапись.
ДаPer-snapshot partition spec. Старые файлы — старый partitioning, новые — новый. Query planning использует оба.
НетНе поддерживается без перезаписи.
НетТребует recreate таблицы.

Ключевой архитектурный паттерн: ID vs Position vs Name

Различия в schema evolution между table formats сводятся к фундаментальному вопросу: как формат идентифицирует колонки.

Механизмы идентификации колонок в table formats
IcebergInteger field-id, уникальный и immutable. Как Protobuf field numbers — ID присваивается при создании и никогда не меняется. Это позволяет rename, reorder, drop без потери маппинга.
Delta Lake (mapping mode)С column mapping mode — Delta присваивает internal column ID и physical name, независимые от логического имени. Без mapping mode — позиционный маппинг (как Parquet).
Hudi / PaimonПреимущественно name-based маппинг. Колонки идентифицируются по имени в schema. Это ограничивает операции: rename ломает маппинг с существующими файлами.

Этот паттерн мы уже видели в уроке 01: Avro использует имена (+ aliases), Protobuf — числовые ID, Parquet/ORC — позиции. Table formats повторяют ту же историю на уровне metadata layer.

Практические рекомендации

Для новых проектов

  1. Если schema evolution критична (аналитический data lake с частыми изменениями) → Iceberg. ID-based tracking + partition evolution = минимальные ограничения.
  2. Если Spark/Databricks — основной движокDelta Lake. Включите column mapping mode (name) сразу при создании таблицы — переключить позже нельзя без миграции.
  3. Если streaming-first (Flink CDC)Paimon. Автоматическая синхронизация schema из upstream sources.
  4. Если уже на Hudi → планируйте schema changes как add-only. Rename/drop — через создание view или materialized table.

Миграция между форматами

Delta Lake, Iceberg и Hudi хранят данные в одних и тех же Parquet файлах. Разница — в metadata. Это позволяет:

  • UniForm (Delta Lake): генерирует Iceberg metadata поверх Delta Parquet файлов
  • Apache XTable (incubating): конвертирует metadata между Delta ↔ Iceberg ↔ Hudi
  • Одни и те же Parquet файлы — три разных metadata view
NOTE

UniForm и XTable не конвертируют данные — они транслируют metadata. Schema evolution семантика при этом определяется целевым форматом. Iceberg metadata даёт ID-based tracking даже поверх Delta-записанных файлов.

Итоги

Schema evolution в table formats — это metadata layer поверх Parquet/ORC, который снимает ограничения нижележащих форматов:

  • Iceberg — наиболее мощный: ID-based tracking, полный набор операций (add/drop/rename/reorder), partition evolution, spec v3
  • Delta Lake — schema enforcement по умолчанию (защита от случайных изменений) + evolution через mergeSchema/column mapping, type widening
  • Hudi — минимальная эволюция (add columns), фокус на upsert/incremental, не на schema changes
  • Paimon — streaming-first: автоматическая синхронизация schema из CDC sources, add/drop/rename

Ключевой architectural insight: способ идентификации колонок (ID vs name vs position) определяет все возможности эволюции — как на уровне serialization formats, так и на уровне table formats.

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

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Delta Lake schema enforcement: DataFrame с колонкой phone, которой нет в таблице. mergeSchema не включён. Что произойдёт?

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

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

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

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