Learning Platform
Глоссарий Troubleshooting
Урок 11.03 · 24 мин
Средний
delta-laketransaction-logdeletion-vectorschange-data-feed

Delta Lake connector: transaction log, deletion vectors, change data feed

Мы разобрали два формата таблиц: Iceberg с деревом метаданных и снапшотами и Hive с метаданными в Metastore до уровня партиции. Третий формат lakehouse — Delta Lake, и это полноценный современный формат таблиц того же класса, что Iceberg. Delta решает те же задачи — атомарность, time travel, row-level DML, — но архитектурой метаданных отличается: вместо дерева снапшотов у него transaction log. Этот урок разбирает устройство Delta-таблицы, механику transaction log, deletion vectors и change data feed, и сравнивает Delta с Iceberg.

Transaction log: журнал изменений вместо дерева снапшотов

Delta-таблица, как и Iceberg, — это файлы данных в Parquet плюс слой метаданных в том же object storage. Но слой метаданных устроен иначе. Сердце Delta-таблицы — transaction log, журнал транзакций. Он лежит в поддиректории _delta_log/ внутри директории таблицы.

Transaction log — это упорядоченная последовательность пронумерованных JSON-файлов: 00000000000000000000.json, 00000000000000000001.json, и так далее. Каждый такой файл — одна транзакция, одно зафиксированное изменение таблицы. Содержимое файла — это не данные, а список действий (actions): «добавлен файл данных X», «удалён файл данных Y», «изменена схема», «изменены свойства таблицы».

Принципиальная идея: чтобы узнать текущее состояние Delta-таблицы, движок читает transaction log с начала и проигрывает все действия по порядку. Транзакция 5 добавила файлы A, B, C. Транзакция 9 удалила файл B, добавила D. Текущий набор файлов — A, C, D. Состояние таблицы — это результат свёртки всего журнала. Это модель event sourcing: состояние не хранится напрямую, оно выводится из последовательности событий.

Transaction log Delta: состояние как свёртка журнала
00...000.jsonПервая транзакция: действия добавления первых файлов данных, задание схемы таблицы
00...001.jsonВторая транзакция: добавлен файл C
00...002.jsonТретья транзакция: файл B удалён — например, после UPDATE части строк
Текущее состояниеСвёртка всех действий по порядку: набор живых файлов A, C — это и есть актуальная таблица

Чтобы не перечитывать тысячи JSON-файлов на каждый запрос, Delta периодически создаёт checkpoint — файл в формате Parquet, который хранит уже свёрнутое состояние таблицы на определённую транзакцию. При чтении движок берёт последний checkpoint и доигрывает только JSON-файлы после него. Это та же идея, что checkpoint в журналах СУБД: не проигрывать журнал с самого начала времён.

Сравните с Iceberg. У Iceberg — дерево: metadata-файл указывает на снапшот, снапшот на manifest list, тот на манифесты. Каждый снапшот хранит полный список файлов своего состояния. У Delta — линейный журнал действий, и состояние выводится их свёрткой. Обе модели дают атомарность, time travel и метаданные уровня файла — но Iceberg хранит состояния, а Delta хранит переходы между ними.

Delta Lake: архитектура Transaction Log Выбор Table Format: Delta Lake vs Iceberg vs Hudi vs Paimon

Как Delta обеспечивает атомарность

Атомарность коммита в Delta — это атомарное создание следующего пронумерованного JSON-файла в _delta_log/. Транзакция, стартовавшая от версии 8, готовит свои действия и пытается записать файл ...009.json. Если запись успешна — транзакция зафиксирована, версия таблицы стала 9.

Конкуренция здесь решается так же оптимистично, как в Iceberg. Если две транзакции стартовали от версии 8, обе хотят создать ...009.json. Создать файл с этим именем сможет только одна — вторая получит конфликт (имя занято), перечитает журнал, увидит чужую транзакцию 9 и повторит попытку, целясь уже в ...010.json. Это оптимистичная конкуренция через гонку за следующее имя файла в журнале.

# etc/catalog/delta.properties
connector.name=delta_lake
hive.metastore=thrift
hive.metastore.uri=thrift://hms:9083
fs.native-s3.enabled=true
s3.endpoint=http://minio:9000
s3.path-style-access=true
# Разрешить процедуру register_table
delta.register-table-procedure.enabled=true

Заметьте: Delta-коннектор тоже использует metastore (HMS или Glue) — но не для хранения списка файлов, а лишь как реестр «имя таблицы -> её директория». Сам список файлов и вся история — в _delta_log/, а не в metastore. Metastore для Delta — это указатель на таблицу, а transaction log — источник правды о её содержимом.

WARNING

Конкурентная запись в Delta на S3 требует внимания. Атомарность зависит от того, что создать файл с уже существующим именем нельзя. Современные S3 это гарантируют через conditional writes, но в части конфигураций или при нескольких писателях используют свойство delta.enable-non-concurrent-writes — его включают, только когда гарантировано, что в таблицу одновременно не пишет несколько процессов. Включить его при реальной конкуренции — риск потери коммита.

Deletion vectors: удаление без переписывания файла

Delta — современный формат, и row-level DELETE и UPDATE он поддерживает. Базовый механизм: чтобы удалить строки из файла, Delta может переписать файл без них и записать в журнал действия «remove старый файл, add новый». Это работает, но дорого — переписывается весь файл ради удаления нескольких строк.

Deletion vectors — оптимизация этого механизма. Вместо переписывания файла данных Delta пишет рядом компактный битовый вектор: битмап позиций строк, помеченных удалёнными в этом файле. Файл данных не трогается. При чтении движок применяет deletion vector — пропускает строки, чьи позиции помечены в векторе.

Выигрыш виден на типичном сценарии. DELETE FROM accounts WHERE status = 'closed' затронул по одной строке в каждом из 500 крупных файлов. Без deletion vectors пришлось бы переписать все 500 файлов. С deletion vectors — записать 500 крошечных векторов, файлы данных остаются как есть. Удаление из часов превращается в секунды.

Это идейно то же, что delete files в Iceberg v2 и delta-директории в Hive ACID: общий принцип всех современных форматов — не трогать большие файлы данных, а фиксировать удаления отдельной компактной структурой. Включается свойством таблицы:

-- Включить deletion vectors при создании таблицы
CREATE TABLE delta.ops.accounts (
  account_id BIGINT,
  status     VARCHAR,
  balance    DECIMAL(12,2)
)
WITH (
  deletion_vectors_enabled = true
);

INSERT INTO delta.ops.accounts VALUES
  (1, 'active', DECIMAL '1000.00'),
  (2, 'closed', DECIMAL '0.00'),
  (3, 'active', DECIMAL '750.00');
-- INSERT: 3 rows

-- DELETE пишет deletion vector, не переписывает файл данных
DELETE FROM delta.ops.accounts WHERE status = 'closed';
-- DELETE: 1 row
Deletion vector: файл данных нетронут, удаления — рядом
Файл данных ParquetКрупный неизменяемый файл со строками; при DELETE он НЕ переписывается
DELETE добавляет вектор рядом
Deletion vectorКомпактный битмап позиций удалённых строк в этом файле; занимает килобайты вместо переписывания мегабайтного файла
движок применяет при чтении
Результат чтенияДвижок читает файл данных и пропускает строки, чьи позиции помечены в deletion vector

Change data feed: поток изменений таблицы

Последняя важная возможность Delta — change data feed (CDF), поток изменений. Обычный SELECT отдаёт текущее состояние таблицы. CDF отвечает на другой вопрос: что именно изменилось между двумя версиями таблицы — какие строки вставлены, какие удалены, какие обновлены.

Зачем это нужно. Представьте downstream-витрину, которая агрегирует данные исходной таблицы. После изменения исходной таблицы пересчитывать витрину целиком дорого. Если знать точную дельту изменений, можно обновить витрину инкрементально — применить только изменения. CDF и даёт эту дельту.

Когда у таблицы включён change data feed, Delta при каждой записи дополнительно фиксирует change-события: для каждой затронутой строки — что с ней произошло (insert, delete, update_preimage — было, update_postimage — стало). Эти события можно прочитать за диапазон версий.

-- Включить change data feed на таблице
ALTER TABLE delta.ops.accounts
  SET PROPERTIES change_data_feed_enabled = true;

CDF — основа инкрементальных пайплайнов: вместо «прочитать всю таблицу заново» — «прочитать, что изменилось с прошлого запуска». Это перекликается с table_changes() и инкрементальным REFRESH материализованных представлений из модуля по Iceberg: разные форматы, одна инженерная идея — построчное отслеживание изменений ради инкрементальности.

Delta и Iceberg: сравнение

АспектDelta LakeIceberg
Модель метаданныхTransaction log — журнал действий, состояние выводится свёрткойДерево снапшотов — каждый снапшот хранит полный список файлов
Ускорение чтения метаданныхCheckpoint (свёрнутое состояние в Parquet)Дерево само избирательно (manifest list -> манифесты)
Атомарность коммитаГонка за следующее имя файла в _delta_log/Compare-and-swap указателя в каталоге
Удаление без переписыванияDeletion vectorsDelete files (v2), deletion vectors (v3)
Time travelFOR VERSION AS OF, FOR TIMESTAMP AS OFFOR VERSION AS OF, FOR TIMESTAMP AS OF
Поток измененийChange data feedtable_changes()
Роль metastoreТолько указатель на директорию таблицыОдин из вариантов каталога

Delta и Iceberg — конкурирующие форматы одного класса. Оба дают атомарность, time travel, row-level DML, метаданные уровня файла. Trino полноценно поддерживает оба, и выбор между ними чаще диктуется экосистемой вокруг (Delta тесно связан с Databricks/Spark, Iceberg — более вендоронейтрален), чем техническим превосходством. Главное для дата-инженера — понимать, что transaction log и дерево снапшотов решают одну задачу двумя разными способами.

Попробуй сам

В песочнице настройте каталог delta (коннектор delta_lake, metastore, MinIO). Упражнение первое: создайте Delta-таблицу accounts, сделайте несколько INSERT, затем посмотрите в MinIO содержимое поддиректории _delta_log/ — найдите пронумерованные JSON-файлы и откройте один, разберите, какие действия (add, remove) в нём записаны. Упражнение второе на deletion vectors: создайте таблицу с deletion_vectors_enabled = true, загрузите данные, выполните DELETE нескольких строк и проверьте через SELECT, что они исчезли из результата; объясните, почему файл данных при этом не переписался. Упражнение третье на time travel: выполните серию изменений, затем SELECT ... FOR VERSION AS OF <n> для разных версий и проследите, как менялось содержимое. Письменно сравните: чем transaction log Delta отличается от дерева снапшотов Iceberg и почему обе модели дают одинаковые гарантии — атомарность и time travel.


Проверка знанийKnowledge check
Как устроен transaction log в Delta Lake, чем эта модель метаданных отличается от дерева снапшотов Iceberg, и что такое deletion vectors?
ОтветAnswer
Transaction log — сердце Delta-таблицы, журнал транзакций в поддиректории _delta_log внутри директории таблицы. Это упорядоченная последовательность пронумерованных JSON-файлов, где каждый файл — одна зафиксированная транзакция, а его содержимое — список действий: добавлен файл данных, удалён файл данных, изменена схема. Чтобы узнать текущее состояние таблицы, движок читает журнал с начала и проигрывает все действия по порядку — состояние это свёртка журнала, модель event sourcing: состояние не хранится напрямую, а выводится из последовательности событий. Чтобы не перечитывать тысячи JSON-файлов, Delta периодически создаёт checkpoint — Parquet-файл со свёрнутым состоянием на определённую транзакцию, и движок доигрывает только файлы после него. Отличие от Iceberg: у Iceberg дерево, где metadata-файл указывает на снапшот, снапшот хранит полный список файлов своего состояния; у Delta линейный журнал действий, и состояние выводится их свёрткой. Iceberg хранит сами состояния, Delta хранит переходы между ними, но обе модели дают атомарность, time travel и метаданные уровня файла. Атомарность коммита в Delta — это атомарное создание следующего пронумерованного JSON-файла: при конкуренции создать файл с занятым именем сможет только одна транзакция, вторая получит конфликт и повторит попытку с следующим номером. Deletion vectors — оптимизация row-level DELETE: вместо переписывания файла данных ради удаления нескольких строк Delta пишет рядом компактный битмап позиций удалённых строк, а файл данных не трогает; при чтении движок применяет вектор и пропускает помеченные строки. Это превращает удаление по строке из 500 крупных файлов из переписывания всех файлов в запись 500 крошечных векторов — та же идея, что delete files в Iceberg v2.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Как Delta Lake определяет текущее состояние таблицы из transaction log?

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

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

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

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