Streaming lakehouse: Paimon vs Iceberg vs Hudi
В 2020-2022 годах появилось три open table format-а — Apache Iceberg, Apache Hudi и Delta Lake (open source часть). Все три обещали “lakehouse” — комбинацию batch data lake (S3 + Parquet) с warehouse-properties (ACID, schema evolution, time travel). Это поколение сделало batch-аналитику на data lake-ах серьёзным конкурентом proprietary warehouse-ам (Snowflake, BigQuery).
Apache Iceberg: глубокое погружение (Spark perspective) Apache Paimon: глубокое погружение (Spark perspective) Lakehouse коннекторы в Flink: Paimon, Iceberg, HudiНо было одно слабое место — streaming. Iceberg хорош для batch, но streaming в нём — это compromised: micro-batch commits, мелкие файлы, проблемы с low-latency reads. Hudi был спроектирован с CDC use case, но и его streaming-возможности имели ограничения.
Apache Paimon (graduate Apache в 2024) появился как streaming-first lakehouse: с самого начала спроектирован для high-throughput continuous ingestion из Flink с low-latency consume того же data. Этот урок: какое место занимает Paimon, чем он отличается от Iceberg/Hudi, и когда что выбирать.
Что такое lakehouse
Сначала контекст. Три поколения analytical data systems:
Data warehouse (1980s-2010s). Teradata, Oracle, потом Snowflake, BigQuery, Redshift. Proprietary format, hardware-software bundled. ACID, schema evolution, fast queries. Не открыт — вендор-lock, проdата expensive.
Data lake (2010s). Files on HDFS/S3. Open formats: CSV, JSON, потом Parquet, ORC. Дёшево, scalable, но без ACID, без schema enforcement. Hive Metastore как weak abstraction. Queries через Spark, Presto, Trino.
Lakehouse (2020s). Open table formats на S3/cloud-storage: Iceberg, Hudi, Delta. ACID transactions, schema evolution, time travel — всё что warehouse имеет, но на open files. Multiple engines читают (Spark, Trino, Flink, Snowflake-external).
Lakehouse не новое хранилище, а новая абстракция над files. Под капотом — Parquet/ORC файлы + manifest файлы + metadata. Engine читает manifest (small) -> знает какие data files (large) ему нужны -> читает только их. Mutations работают через snapshot-isolation: новые versions создают новые files, манифест обновляется atomically.
Что такое streaming lakehouse
Classic lakehouse решает batch workloads. Streaming — это другая категория проблем:
- Continuous ingestion: не один insert каждые часы, а тысячи rows/sec из Flink/Kafka.
- Low-latency reads: query видит новые данные через секунды, не часы.
- Mutations as first-class: streaming CDC из source DB (upserts, deletes), а не append-only.
- Hybrid queries: один и тот же таблица — и для real-time dashboard, и для batch ML training.
Iceberg с такими workload-ами справляется плохо. Каждый Flink checkpoint = 1 commit = 1 snapshot. С checkpoint interval 1 минуту получаем 1440 snapshots/day. Manifest growth -> query planning slow. Мелкие data files (Flink subtask за minute не успевает записать большой parquet) -> query reads много мелких files, slow. Solution — async compaction process, но он сам конкурирует с writer-ами.
Paimon спроектирован специально для streaming:
- LSM-tree storage: маленькие L0 files compacted-ятся в большие L1/L2. Сразу аналогия с RocksDB.
- Snapshot per checkpoint: но snapshots cheap, и automatic compaction поверх.
- Streaming reads: consumer может subscribe на таблицу, получать changes по мере commit-ов (как Kafka topic).
- Built-in changelog: операция UPDATE на primary-key table генерирует -U, +U records — это можно consume downstream-у через streaming read.
Сравнение: Paimon, Iceberg, Hudi
Streaming-first vs batch-first
Дизайн различия видны на простом сценарии:
Сценарий: Flink job, который consumes Kafka topic с CDC из MySQL. 50 tables, average 500 events/sec на table, peak 5000. Sink в lakehouse. Downstream — два consumer-а: BI dashboard (Trino queries каждые 30 sec) и feature store (streaming read continuous).
С Iceberg:
- Flink checkpoint interval 1 минуту -> 60 commits/час -> 1440 commits/день на 50 таблиц = 72000 snapshots.
- Каждый commit = small parquet file (500 records * 60 sec = 30000 rows ≈ 100KB Parquet — слишком мало).
- Через сутки в таблице 1440 файлов по 100KB = 144MB. Query plan = чтение 1440 файлов parallel — каждый open() = S3 latency, total 5-10 секунд только на open-ы.
- Решение: scheduled compaction Spark/Flink job, каждый час merge мелкие в большие. Но это external pipeline, его нужно maintain.
- Streaming read для feature store — Iceberg поддерживает (
SELECT ... FROM table /*+ OPTIONS('streaming'='true') */), но reading new snapshots каждые seconds — overhead и low-latency проблематичен.
С Paimon:
- Тот же checkpoint = snapshot, но Paimon делает в background compaction L0 -> L1 -> L2.
- Через сутки таблица имеет ~10 больших файлов в L2 (compacted) + новые L0 files. Reader читает L2 + applying changes from L0 (in-memory).
- Streaming read native — connector подписывается на snapshots, читает changes incrementally. Это эффективнее чем повторные SELECT.
- Trino через Paimon connector читает latest snapshot (или specific snapshot для time-travel). Снова — L2 files давно compacted, query быстрая.
Paimon разрабатывался изначально с этим в виду. Iceberg — fits batch workloads, streaming retrofitted позже.
Когда что выбирать
Choice matrix:
| Use case | Best fit | Reason |
|---|---|---|
| Append-only batch ETL (web logs, IoT) | Iceberg | Multi-engine (Spark/Trino), mature ecosystem |
| Open data sharing (vendor-neutral) | Iceberg | Wide engine support, AWS Glue catalog standard |
| Snowflake/BigQuery external tables | Iceberg | Native external table support |
| CDC ingestion from RDBMS | Paimon | Built-in PK support, streaming reads, changelog |
| Real-time analytics (sub-min latency reads) | Paimon | LSM-tree designed for this |
| Flink streaming primary | Paimon | Flink-Paimon team works together, deepest integration |
| Mixed batch + streaming на одной таблице | Paimon | Unified, single source of truth |
| Hudi-legacy migration | Hudi | Stay if invested |
| Iceberg-legacy migration | Iceberg | Stay if invested, или migrate to Paimon если streaming pain |
Не “одно лучше всех” — это разные tools для разных проблем. Большие компании используют оба: Iceberg для batch warehouse-данных, Paimon для streaming/CDC.
Flink + lakehouse integration
Flink — главный engine, который читает/пишет во все три формата:
// Iceberg
FlinkSink.forRowData(stream)
.table(table)
.tableLoader(tableLoader)
.writeParallelism(8)
.append();
// Paimon
PaimonSink.<RowData>builder()
.withCatalog(catalog)
.withTable("orders")
.build();
// Hudi
HoodiePipeline.Builder builder = HoodiePipeline.builder("h_orders")
.column("id BIGINT")
.column("name STRING")
.pk("id")
.options(...);
Под капотом все три используют FLIP-143 Sink V2 protocol (Writer + Committer + GlobalCommitter). GlobalCommitter создаёт snapshot/commit atomically. Это даёт exactly-once.
SQL access одинаков:
-- Catalog setup
CREATE CATALOG paimon_cat WITH (
'type' = 'paimon',
'warehouse' = 's3://bucket/paimon/'
);
USE CATALOG paimon_cat;
-- Streaming write
INSERT INTO orders SELECT * FROM kafka_orders;
-- Batch read
SELECT * FROM orders WHERE date = '2026-05-19';
-- Streaming read
SELECT * FROM orders /*+ OPTIONS('streaming'='true') */;
-- Time travel
SELECT * FROM orders /*+ OPTIONS('scan.snapshot-id'='12345') */;
Materialized tables (Flink 2.0+)
Это новая концепция в Flink, которая поверх Paimon (или другого lakehouse) даёт еще один уровень абстракции:
CREATE MATERIALIZED TABLE order_summary
REFRESH MODE = CONTINUOUS -- streaming или batch refresh
FRESHNESS = INTERVAL '5' MINUTE -- максимальное отставание от source
AS
SELECT date, count(*), sum(amount)
FROM orders
GROUP BY date;
Flink сам решает: запустить continuous streaming job (FRESHNESS = seconds) или scheduled batch job (FRESHNESS = hours). Storage — Paimon (или другой supported lakehouse). Это unified API для batch и streaming aggregations. Подробнее в уроке 04-materialized-tables.
Apache Fluss (incubating)
Кроме lakehouse, в экосистеме Flink появляется Fluss — это streaming-first storage (не table format, а complete storage layer). Альтернатива Kafka, оптимизирована для analytics:
- Column-oriented (vs Kafka row-oriented) — query даёт server-side projection.
- Sub-second consume latency (как Kafka), но при этом columnar — фильтры на server side.
- Integrated с Paimon — Fluss может materialize в Paimon table.
Fluss vs Kafka: Kafka — universal log storage, Fluss — narrow analytical streaming. Подробнее в уроке 05-fluss-streaming-storage.
Иногда возникает confusion: “Paimon” и “Fluss” — это связанные но разные вещи. Paimon — lakehouse storage (как Iceberg). Fluss — streaming storage (как Kafka). Они дополняют друг друга: Fluss как hot streaming layer, Paimon как cold lakehouse layer. Flink job может писать в Fluss для real-time, и параллельно/eventually materialize в Paimon для batch analytics.
Production-перспектива: миграция и interop
Многие компании сейчас в процессе migration с Iceberg/Hudi на Paimon (или наоборот). Practical advice:
Не мигрируй без причины. Если Iceberg работает и нет pain — оставь. Migration cost (data copy, pipeline rewrite, testing) большой.
Мигрируй если streaming pain. Если у тебя Iceberg с 1000+ snapshots/час и постоянные compaction job-ы fighting writers — Paimon уберёт боль.
Try interop. Paimon может read Iceberg (через Paimon Iceberg compatibility). Можно делать gradual migration: новые tables на Paimon, старые на Iceberg, постепенно сливать.
Catalog wars. Iceberg ecosystem стандартизировался на AWS Glue / Nessie / Polaris. Paimon ещё имеет свой catalog (file-based or Hive). Если у тебя Glue / Snowflake-external setup -> Iceberg natural fit.