Delta Lake vs Apache Iceberg у 2026: Архітектура Lakehouse та Питання на Співбесідах

Комплексне порівняння Delta Lake та Apache Iceberg — двох провідних відкритих форматів таблиць для сучасної архітектури data lakehouse. Ключові відмінності, практичні приклади коду та типові питання на співбесідах для інженерів даних.

Порівняння Delta Lake та Apache Iceberg для архітектури data lakehouse

Delta Lake та Apache Iceberg представляють два домінуючі відкриті формати таблиць, що забезпечують сучасні архітектури data lakehouse у 2026 році. Обидва вирішують одну й ту саму фундаментальну проблему — впровадження транзакцій ACID, еволюції схеми та подорожей у часі до хмарного об'єктного сховища — проте застосовують різні архітектурні підходи, що мають значення для продуктивних навантажень.

Швидке Порівняння

Delta Lake найкраще підходить для Spark-нативних середовищ із тісною інтеграцією Databricks, тоді як Iceberg пропонує ширшу сумісність із рушіями (Spark, Trino, Flink, Dremio) та гнучкіше партиціювання завдяки прихованим партиціям та еволюції партицій.

Delta Lake vs Iceberg: Ключові Архітектурні Відмінності

Обидва формати зберігають дані як файли Parquet в об'єктному сховищі, однак їхні шари метаданих суттєво відрізняються.

Delta Lake використовує журнал транзакцій (_delta_log/), що містить JSON-файли, які реєструють кожну зміну. Кожен коміт створює новий JSON-файл, а періодичні контрольні точки консолідують їх у формат Parquet для швидшого читання. Специфікація протоколу Delta Lake визначає версіоновані функції читача/записувача для сумісності з майбутніми версіями.

Apache Iceberg підтримує ієрархію файлів метаданих: JSON-файл метаданих, що вказує на списки маніфестів, які вказують на маніфести зі статистикою на рівні файлів. Ця конструкція дозволяє predicate pushdown на етапі планування — рушій запитів пропускає цілі файли до читання будь-яких даних.

| Функція | Delta Lake | Apache Iceberg | |---------|------------|----------------| | Журнал Транзакцій | JSON + Parquet контрольні точки | JSON метаданих + файли маніфестів | | Еволюція Партицій | Потребує перезапису | На місці без перезапису | | Підтримка Рушіїв | Spark, Trino, Flink | Spark, Trino, Flink, Dremio, Athena | | Подорож у Часі | На основі версій | На основі снепшотів із branch/tag | | Еволюція Схеми | Додавання/перейменування колонок | Додавання/перейменування/зміна порядку/розширення колонок |

Приховані Партиції: Ключова Перевага Iceberg

Традиційне партиціювання у стилі Hive експонує колонки партицій у запитах (WHERE year=2026 AND month=7), пов'язуючи фізичний макет із синтаксисом SQL. Приховані партиції Iceberg розділяють ці аспекти.

python
# iceberg_partition_example.py
from pyiceberg.catalog import load_catalog
from pyiceberg.schema import Schema
from pyiceberg.types import StringType, TimestampType, LongType, NestedField
from pyiceberg.partitioning import PartitionSpec, PartitionField
from pyiceberg.transforms import MonthTransform

# Визначення схеми
schema = Schema(
    NestedField(1, "event_id", LongType(), required=True),
    NestedField(2, "event_time", TimestampType(), required=True),
    NestedField(3, "user_id", StringType(), required=True),
    NestedField(4, "event_type", StringType(), required=False),
)

# Прихована партиція по місяцю, витягнутому з event_time
partition_spec = PartitionSpec(
    PartitionField(
        source_id=2,  # поле event_time
        field_id=1000,
        transform=MonthTransform(),
        name="event_month"
    )
)

catalog = load_catalog("glue", **{"type": "glue"})
catalog.create_table(
    identifier="analytics.events",
    schema=schema,
    partition_spec=partition_spec
)

Запити фільтрують безпосередньо по event_time — рушій автоматично застосовує обрізку партицій без експонування колонки партиції в умові WHERE. Еволюція партицій дозволяє таблиці перейти з місячного партиціювання на денне без перезапису існуючих даних.

Транзакції ACID та Оптимістична Паралельність у Delta Lake

Delta Lake реалізує серіалізовану ізоляцію за допомогою оптимістичного контролю паралельності. Записувачі перевіряють журнал транзакцій перед комітом для виявлення конфліктів.

python
# delta_concurrent_writes.py
from delta import DeltaTable
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
    .config("spark.sql.catalog.spark_catalog", 
            "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
    .getOrCreate()

# Паралельна операція MERGE з автоматичним вирішенням конфліктів
delta_table = DeltaTable.forPath(spark, "s3://bucket/events")

# Злиття вхідного батчу з існуючими даними
delta_table.alias("target").merge(
    source=incoming_df.alias("source"),
    condition="target.event_id = source.event_id"
).whenMatchedUpdateAll() \
 .whenNotMatchedInsertAll() \
 .execute()

# Виявлення конфліктів відбувається автоматично під час коміту
# Невдалі транзакції повторюються з оновленим снепшотом

Правила вирішення конфліктів Delta Lake дозволяють паралельним додаванням успішно завершуватися, водночас серіалізуючи конфліктуючі оновлення до тієї ж партиції.

Порівняння Подорожей у Часі та Версіювання Даних

Обидва формати підтримують подорожі у часі, але з різною семантикою.

sql
-- Delta Lake: Запит за номером версії
SELECT * FROM events VERSION AS OF 42;

-- Delta Lake: Запит за часовою міткою
SELECT * FROM events TIMESTAMP AS OF '2026-07-15 10:00:00';

-- Iceberg: Запит за ID снепшота
SELECT * FROM analytics.events FOR SYSTEM_VERSION AS OF 8823749234;

-- Iceberg: Запит за часовою міткою
SELECT * FROM analytics.events FOR SYSTEM_TIME AS OF TIMESTAMP '2026-07-15 10:00:00';

Iceberg 1.5+ додає розгалуження та тегування — можна створювати іменовані гілки для ізольованого експериментування, а потім зливати або відкидати. Delta Lake досягає подібних робочих процесів через поверхневі клони.

sql
-- Iceberg: Створення гілки для тестування
ALTER TABLE analytics.events CREATE BRANCH experiment;

-- Запис у гілку без впливу на main
INSERT INTO analytics.events.branch_experiment 
SELECT * FROM staging.events WHERE event_type = 'test';

-- Злиття гілки до main
CALL system.fast_forward('analytics.events', 'main', 'experiment');

Готовий до співбесід з Data Engineering?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Матриця Сумісності Рушіїв Запитів

Сумісність рушіїв визначає вибір формату для багатьох організацій.

| Рушій | Підтримка Delta Lake | Підтримка Iceberg | |-------|---------------------|-------------------| | Apache Spark | Нативна (Databricks, OSS) | Нативна | | Trino/Presto | Конектор | Нативна | | Apache Flink | Конектор | Нативна (1.16+) | | Dremio | Лише читання | Нативна | | AWS Athena | Обмежена | Нативна | | Snowflake | Зовнішні таблиці | Нативна (Iceberg таблиці) | | BigQuery | Зовнішні таблиці | BigLake Iceberg |

Для середовищ лише зі Spark тісніша інтеграція Delta Lake пропонує кращу продуктивність. Багаторушійні архітектури виграють від ширшої сумісності Iceberg — REST Catalog Iceberg забезпечує нейтральний до постачальника API для операцій каталогу.

Техніки Оптимізації Продуктивності

Обидва формати потребують операцій обслуговування для оптимальної продуктивності запитів.

python
# delta_optimize.py
from delta import DeltaTable

delta_table = DeltaTable.forPath(spark, "s3://bucket/events")

# Компактування малих файлів (bin-packing)
delta_table.optimize().executeCompaction()

# Z-order для багатоколонкових предикатів
delta_table.optimize().executeZOrderBy("user_id", "event_time")

# Видалення старих версій (vacuum)
delta_table.vacuum(retentionHours=168)  # Зберігати 7 днів
sql
-- Iceberg: Перезапис файлів даних для компактування
CALL catalog.system.rewrite_data_files(
  table => 'analytics.events',
  strategy => 'binpack',
  options => map('target-file-size-bytes', '134217728')
);

-- Iceberg: Видалення застарілих снепшотів
CALL catalog.system.expire_snapshots(
  table => 'analytics.events',
  older_than => TIMESTAMP '2026-07-01 00:00:00',
  retain_last => 10
);

Z-ordering групує пов'язані дані разом, покращуючи ефективність predicate pushdown. Обидва формати виграють від компактування файлів при інгестії багатьох малих файлів із потокових джерел.

Типові Питання на Співбесідах про Data Lakehouse

Підготовка до часто задаваних питань на співбесідах для інженерів даних.

П: Коли ви обрали б Iceberg замість Delta Lake?

Iceberg краще підходить коли: (1) кілька рушіїв запитів звертаються до одних і тих же таблиць (Spark, Trino, Flink), (2) схеми партицій повинні еволюціонувати без перезапису даних, (3) організація віддає перевагу нейтральним до постачальника відкритим стандартам. Delta Lake найкращий у Databricks-центричних архітектурах або чистих Spark-середовищах, де Unity Catalog забезпечує governance.

П: Як Iceberg досягає еволюції партицій без перезапису даних?

Iceberg зберігає специфікації партицій як метадані. Коли специфікація змінюється, нові дані використовують нове партиціювання, тоді як існуючі дані зберігають оригінальний макет. Планувальник запитів читає всі специфікації партицій і застосовує правильну логіку обрізки для кожної групи файлів.

П: Поясніть механізм контрольних точок Delta Lake.

Delta Lake створює файли контрольних точок за замовчуванням кожні 10 комітів. Контрольна точка консолідує журнал транзакцій в один файл Parquet для швидшого читання. Файл _last_checkpoint вказує на останню контрольну точку, і читачі реконструюють стан, читаючи контрольну точку плюс будь-які наступні JSON-коміти.

П: Що таке copy-on-write та merge-on-read?

Copy-on-write (COW) перезаписує цілі файли при оновленнях — вища вартість запису, але швидше читання. Merge-on-read (MOR) записує дельти окремо, зливаючи під час запиту — нижча затримка запису, але накладні витрати на читання. Iceberg підтримує обидва; Delta Lake переважно використовує COW із deletion vectors для м'яких видалень в останніх версіях.

Ці патерни варто практикувати на питаннях для співбесід з інженерії даних на SharpSkill для закріплення розуміння перед співбесідами.

Шлях Міграції: Конвертація Між Форматами

Організації іноді потребують міграції між форматами. Обидва підтримують утиліти конвертації.

python
# Конвертація Delta в Iceberg за допомогою Spark
spark.sql("""
    CALL catalog.system.snapshot(
        source_table => 'delta.`s3://bucket/delta_events`',
        table => 'analytics.events_iceberg'
    )
""")

# Конвертація Iceberg в Delta
from delta.tables import DeltaTable

iceberg_df = spark.read.format("iceberg").load("catalog.analytics.events")
iceberg_df.write.format("delta").save("s3://bucket/delta_events")

Повна міграція вимагає ретельної валідації типів схеми, семантики партиціювання та сумісності downstream-споживачів.

Висновок

  • Delta Lake найкраще підходить для Spark-нативних навантажень, середовищ Databricks та коли Unity Catalog governance відповідає потребам організації
  • Iceberg слід обирати для багаторушійних архітектур, вимог еволюції партицій та хмарно-агностичних розгортань
  • Обидва формати забезпечують транзакції ACID, подорожі у часі та еволюцію схеми — правильний вибір залежить від існуючої інфраструктури та різноманітності рушіїв запитів
  • Підготовка до співбесід повинна охоплювати внутрішню структуру журналу транзакцій, оптимізацію обрізки партицій та компроміси COW vs MOR
  • Компактування файлів та закінчення терміну дії снепшотів залишаються критичними завданнями обслуговування незалежно від вибору формату

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

#delta-lake
#apache-iceberg
#data-lakehouse
#інженерія-даних
#співбесіда

Поділитися

Пов'язані статті