Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 14.03 · 25 мин
Средний
LakehousePaimonIcebergHudiStreamingSinkACID

Lakehouse — Paimon, Iceberg, Hudi

Lakehouse-форматы — это попытка совместить гибкость data lake (дешёвое объектное хранилище, открытые форматы Parquet/ORC) с транзакционностью data warehouse (ACID, schema evolution, time travel). Flink — один из движков, который пишет в эти форматы; на read-стороне их умеют все: Spark, Trino, Snowflake, ClickHouse, DuckDB.

В 2026 году в Flink-экосистеме доминируют три формата: Apache Paimon (бывший Flink Table Store, ставший верховым projectом ASF), Apache Iceberg и Apache Hudi. Каждый сделан с своими дизайн-решениями, и выбор не очевиден. Этот урок — попытка дать осмысленный гайд.


Зачем lakehouse

Классический рисёрс: продакшен Kafka -> Flink -> Postgres (или ClickHouse). Работает для оперативного запроса; не работает для:

  • Долгосрочного хранения (ClickHouse дорогой по storage, Postgres плохо tunable for analytics).
  • Ad-hoc SQL аналитики на исторических данных от разных команд.
  • Time-travel запросов (“какие у нас были балансы две недели назад”).
  • Schema evolution без блокировок (нельзя ALTER TABLE в продакшене Postgres на терабайтной таблице).

Lakehouse-формат решает эти проблемы. Данные лежат в S3/GCS/HDFS как Parquet-файлы, метаданные хранят историю всех версий (manifests, snapshots), движок выбирает корректный snapshot для запроса.


Apache Paimon

Paimon — относительно молодой формат (родился как Flink Table Store в 2022, стал Apache top-level в 2024). Дизайн-цель: streaming-first хранилище, нативно для Flink. Использует LSM-tree (Log-Structured Merge) для эффективных upserts с мелкими incremental commits.

Paimon как LSM-tree lakehouse — internals

Сильные стороны:

  • Streaming sink работает на любой частоте commits (от секунд до минут). Иные форматы (Iceberg) предпочитают minutes из-за overhead’а большого количества snapshots.
  • Changelog producer — Paimon может генерировать CDC-changelog (для downstream Flink-стримов, читающих из Paimon как из source).
  • Merge engines — настраиваемые: deduplicate (последний по primary key), partial-update (multiple stream sources обновляют разные колонки одного row), aggregation (агрегация при merge).
  • Embedded compaction — Flink job может сам автоматически делать compaction (объединение мелких файлов).

Слабые стороны:

  • Сравнительно молодой — экосистема меньше Iceberg (Snowflake не читает, ClickHouse поддерживает через bridge).
  • Меньше документации, реже встречается в job-постингах.
CREATE CATALOG paimon WITH (
  'type' = 'paimon',
  'warehouse' = 's3://lake/paimon/'
);

USE CATALOG paimon;

CREATE TABLE user_metrics (
  user_id STRING,
  click_count BIGINT,
  last_click_time TIMESTAMP_LTZ(3),
  dt STRING,
  PRIMARY KEY (dt, user_id) NOT ENFORCED
) PARTITIONED BY (dt) WITH (
  'bucket' = '8',
  'merge-engine' = 'partial-update',
  'changelog-producer' = 'lookup',
  'snapshot.time-retained' = '7 d'
);

Apache Iceberg

Iceberg — самый “vendor-neutral” формат. Спецификация открыта, реализаций множество: Java, Python, Rust. Поддерживается ВСЕМИ серьёзными движками: Snowflake, BigQuery, Redshift, Databricks, Trino, Presto, Spark, Flink, ClickHouse, DuckDB, Athena, EMR.

Apache Iceberg — глубокое погружение

Сильные стороны:

  • Универсальная читаемость — таблица, написанная Flink, мгновенно доступна для Snowflake, Spark, ClickHouse.
  • Хорошая batch-производительность — оптимизирован для большого числа batch-запросов с эффективным partition pruning.
  • Богатый ecosystem REST Catalog — Polaris (Snowflake), Lakekeeper, Tabular (теперь Apache, поглощён в Iceberg сообщество).

Слабые стороны:

  • Минимальный commit interval ~1 минута (раньше было 5 минут, сейчас оптимизированы, но всё ещё хуже Paimon для streaming с sub-minute latency).
  • Upsert/CDC слабее: Iceberg-spec V3 добавил row-level deletes и position deletes, но компакция этих deletes сложнее чем в Paimon.
  • Changelog stream ограничен: нативный CDC source из Iceberg работает, но менее зрел.
CREATE CATALOG iceberg WITH (
  'type' = 'iceberg',
  'catalog-type' = 'rest',
  'uri' = 'https://lakekeeper.internal/api/iceberg',
  'warehouse' = 's3://lake/iceberg/',
  'token' = '...'
);

USE CATALOG iceberg;

CREATE TABLE orders (
  order_id BIGINT,
  customer_id BIGINT,
  amount DECIMAL(10, 2),
  order_date DATE
) PARTITIONED BY (order_date) WITH (
  'write.format.default' = 'parquet',
  'write.target-file-size-bytes' = '536870912',
  'write.distribution-mode' = 'hash'
);

-- Append-only sink (Iceberg v2 spec)
INSERT INTO orders SELECT * FROM kafka_orders;

Apache Hudi

Hudi — старейший из трёх (2017), родился в Uber для CDC use-case. Был долго лидером для streaming-CDC в lakehouse. Сейчас (2026) популярность смещается в сторону Paimon (для Flink-стека) и Iceberg (для multi-engine), но Hudi остаётся силён там, где он исторически вырос: Spark + CDC.

Сильные стороны:

  • CDC-first дизайн — Copy-on-Write (CoW) и Merge-on-Read (MoR) режимы. MoR оптимизирован для частых обновлений мелких порций.
  • Bootstrap-fra-existing-Parquet — превратить старый Parquet-датасет в Hudi-таблицу без перезаписи.
  • Богатые table services — clustering, archiving, indexing встроены.

Слабые стороны:

  • Сложность конфигурации — много знобов, легко shot-in-foot. Steep learning curve.
  • Slow adoption за пределами Spark — Flink интеграция есть, но менее зрелая, чем Iceberg или Paimon.
  • Snowflake/BigQuery не читают Hudi нативно (требуется внешний integration).
CREATE TABLE hudi_orders (
  order_id BIGINT,
  customer_id BIGINT,
  amount DECIMAL(10, 2),
  ts TIMESTAMP(3),
  PRIMARY KEY (order_id) NOT ENFORCED
) WITH (
  'connector' = 'hudi',
  'path' = 's3://lake/hudi/orders',
  'table.type' = 'MERGE_ON_READ',
  'write.operation' = 'upsert',
  'precombine.field' = 'ts',
  'hive_sync.enabled' = 'true',
  'hive_sync.mode' = 'hms',
  'hive_sync.metastore.uris' = 'thrift://metastore:9083'
);

Сравнение

Дизайн-различия трёх форматов
PaimonLSM-tree-based. Делает sub-minute streaming commits эффективно. Embedded compaction. Изначально дизайнен для Flink streaming.
Use caseFlink streaming pipeline (CDC sink, агрегации с upsert), где нужна низкая latency.
IcebergSnapshot-based. Каждый commit — новый snapshot с manifest файлами. Универсальная поддержка движков.
Use caseCentralized lakehouse table, читаемая Spark, Flink, Trino, Snowflake. Batch и near-realtime (1-5 минут).
HudiCopy-on-Write vs Merge-on-Read. MoR — log file + base file, merge при read. Богатые table services.
Use caseSpark CDC pipeline (Uber-style use-case). Migration of existing Parquet datasets. Менее популярен для Flink.
ХарактеристикаPaimonIcebergHudi
ДизайнLSM-tree, streaming-firstSnapshot manifest, batch-leaningCoW / MoR, CDC-leaning
Min commit intervalсекунды30s-1mсекунды (MoR)
Upsertfirst-class, multiple merge enginesrow/position deletes (V3)first-class, ключевой use-case
CDC sourcefirst-class (changelog producer)new (limited)first-class
Schema evolutionподдержкаподдержкаподдержка
Time travelподдержкаподдержка (snapshots)поддержка (commits)
EcosystemFlink, Spark, ClickHouse, TrinoAll major enginesSpark first, Flink second
Compactionembedded в Flink jobexternal job или engineservice + scheduled jobs
Сложностьсредняянизкая для batchвысокая
Зрелость для Flinkhighmedium-highmedium

Полный production-пример: Flink job читает из Kafka и пишет в Paimon с agregation merge engine.

-- Source
CREATE TABLE clicks (
  user_id STRING,
  url STRING,
  event_time TIMESTAMP_LTZ(3),
  WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
) WITH (
  'connector' = 'kafka',
  'topic' = 'clicks',
  'properties.bootstrap.servers' = 'kafka:9092',
  'scan.startup.mode' = 'group-offsets',
  'properties.group.id' = 'flink-paimon-writer',
  'format' = 'json'
);

-- Paimon catalog
CREATE CATALOG paimon WITH (
  'type' = 'paimon',
  'warehouse' = 's3://lake/paimon/',
  's3.endpoint' = 'https://s3.eu-central-1.amazonaws.com'
);

-- Paimon sink with aggregation merge engine
CREATE TABLE paimon.analytics.user_daily_clicks (
  dt STRING,
  user_id STRING,
  click_count BIGINT,
  unique_urls BIGINT,
  PRIMARY KEY (dt, user_id) NOT ENFORCED
) PARTITIONED BY (dt) WITH (
  'bucket' = '16',
  'merge-engine' = 'aggregation',
  'fields.click_count.aggregate-function' = 'sum',
  'fields.unique_urls.aggregate-function' = 'sum',
  'changelog-producer' = 'lookup',
  'snapshot.time-retained' = '7 d',
  'compaction.min.file-num' = '5',
  'compaction.max.file-num' = '50'
);

-- Pipeline
INSERT INTO paimon.analytics.user_daily_clicks
SELECT
  DATE_FORMAT(event_time, 'yyyy-MM-dd') AS dt,
  user_id,
  1 AS click_count,
  1 AS unique_urls
FROM clicks;

Что здесь работает:

  • bucket = 16 — Paimon шардирует данные по PK hash в 16 buckets. Каждый bucket — отдельный LSM tree файл.
  • merge-engine = aggregation — при merge нескольких записей с одним PK Paimon применяет агрегацию (sum здесь).
  • changelog-producer = lookup — Paimon генерирует CDC-changelog при merges, downstream Flink jobs могут читать его как source.
  • snapshot.time-retained = 7 d — старые snapshots очищаются через 7 дней (для time travel).
TIP

Aggregation merge engine — мощная Paimon-фича. Вместо того чтобы делать GROUP BY в Flink (с unbounded state риском), вы делаете “1 click per insert” в Flink (stateless) и aggregation делается на read-side при merge файлов. State на Flink-стороне — нулевой, агрегация инкрементальная.


Iceberg sink example

INSERT INTO iceberg.warehouse.orders_partitioned
SELECT
  order_id,
  customer_id,
  amount,
  CAST(order_time AS DATE) AS order_date
FROM kafka_orders
WHERE order_time > '2026-01-01';

Iceberg делает commit каждые checkpoint-interval секунд. При checkpoint-interval=60s — один commit в минуту. Файлы накапливаются — нужна периодическая компакция (через Spark или Trino).


Как выбрать

Сводный decision tree:

  1. Streaming pipeline с CDC внутри Flink-стека? — Paimon. Низкая latency, нативная Flink-интеграция.
  2. Centralized lakehouse читаемый Spark/Trino/Snowflake? — Iceberg. Vendor-neutral, full ecosystem.
  3. Legacy Spark/Hudi проект, миграция не оправдана? — Hudi.
  4. Не уверены, в каком направлении проект пойдёт? — Iceberg (самый универсальный, наименьший lock-in).

Попробуй сам

  1. Возьми Paimon CDC pipeline (Kafka -> Flink -> Paimon). Какие настройки compaction нужны, чтобы файлы не накапливались?
  2. Сравни время записи 10M rows в Iceberg vs Paimon с одинаковым checkpoint interval = 30s. Что повлияет на разницу?
  3. Подумай: если ваша organization уже использует Snowflake для BI — какой формат лучше использовать для lakehouse-таблиц, к которым Snowflake должен иметь доступ?
Проверка знанийKnowledge check
Команда строит pipeline, который consume CDC events из MySQL через Debezium-Kafka, обогащает и пишет в lakehouse. Требования: (1) downstream Spark batch jobs должны читать таблицы каждые 30 минут; (2) BI-аналитики через Snowflake тоже должны видеть данные; (3) latency Flink-to-lakehouse не критична, 5 минут приемлемо. Какой формат выбрать и почему?
ОтветAnswer
Лучший выбор — Iceberg. Логика: (1) Snowflake first-class читает Iceberg (с 2022 года), нативно работает с REST catalog (Snowflake Polaris) — это решающий фактор, потому что Paimon Snowflake не читает напрямую (можно через Spark bridge но это сложно); (2) Spark batch jobs читают Iceberg отлично — Apache Spark — second-class Iceberg engine; (3) 5-минутная latency не требует sub-minute streaming Paimon — Iceberg commit interval 1 минута укладывается с запасом; (4) Iceberg upsert/CDC через row-level deletes (V2 spec) или position deletes (V3) — медленнее Paimon, но для CDC обновлений нескольких тысяч rows/sec и checkpoint interval = 1min справляется. Paimon был бы лучше, если бы требовалась sub-second latency или downstream был только Flink, но мульти-engine multi-cloud требование делает Iceberg более безопасным выбором. Hudi отпадает потому, что Snowflake его нативно не читает.

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

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

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

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