Streaming Lakehouse паттерн
До 2024 года data-инфраструктура крупных компаний жила в двух мирах. Один мир — Kafka log: мутабельный stream событий, retention 7 дней, миллионы сообщений в секунду, потребители — операционные сервисы. Другой мир — Lakehouse (Iceberg / Delta / Hudi): неизменяемые таблицы на S3 / GCS, retention годы, terabytes данных, потребители — Spark, Trino, DuckDB, ClickHouse. Между этими мирами стоял Kafka Connect ETL: батч-загрузка раз в час, дублирование данных, lag 30-60 минут, отдельные команды на эксплуатацию.
Streaming Lakehouse — это паттерн, который сливает оба мира в одну таблицу. Kafka-топик и Iceberg-таблица становятся двумя представлениями одного и того же данных. Producer пишет в Kafka, через минуты те же сообщения уже в Iceberg, доступны для SQL-запросов из Trino. Никакого дополнительного pipeline. Никакого дублирования.
В этом уроке: что такое stream-table duality, какие есть три способа реализации (Iceberg Sink Connector, Tableflow, Debezium Server), почему Apache Paimon это другая философия, и какие production-ловушки убивают наивные внедрения.
Контекст: Kafka log vs Lakehouse
Чтобы понять паттерн, начнём с фундаментального различия двух подсистем.
| Свойство | Kafka log | Lakehouse (Iceberg / Delta) |
|---|---|---|
| Назначение | Transient transport: события в реальном времени | Permanent storage: историческая аналитика |
| Retention | Часы / дни (обычно 7 дней) | Годы / навсегда |
| Хранилище | SSD на брокерах (дорого) | Object storage S3 / GCS (дёшево) |
| Format | Avro / Protobuf / JSON в сегментах .log | Parquet / ORC в snapshot-based таблицах |
| Query | Sequential read с offset | Random access, predicate pushdown, time-travel |
| Throughput | Миллионы сообщений / сек / partition | Зависит от числа файлов и compaction |
| Latency | Миллисекунды | Минуты (commit interval) — часы (batch ETL) |
| Consumers | Сервисы (микросервисы, stream processing) | Аналитики (Spark, Trino, DuckDB, BI) |
Классический ETL-подход: пишем в Kafka, через 30-60 минут JDBC/S3 Sink Connector батчем сливает в Lakehouse. Проблемы: дублирование данных (одни события живут в двух местах), drift схем (Kafka schema vs Iceberg schema), часовой lag для аналитики, отдельный pipeline для эксплуатации. Streaming Lakehouse решает это через унифицированную абстракцию.
Stream-Table Duality: фундамент паттерна
Stream-Table Duality — концепция, известная ещё со времён Kafka Streams: stream может быть представлен как table, и наоборот. Изменения, применяемые к таблице, материализуются как stream. Stream и table — две стороны одной монеты.
Iceberg добавляет к этой дуальности новый уровень: одни и те же данные могут одновременно быть таблицей и stream-ом. Через snapshot references один потребитель читает таблицу как unbounded stream (новые snapshot-ы как новые события), второй — как point-in-time bounded таблицу, третий — как ветвь от общего предка.
Producer → Kafka topic "orders.created"
│
│ (zero-copy materialization)
▼
Iceberg table "warehouse.orders_created"
│
├─ Trino: SELECT * WHERE created_at > '2026-04-01'
├─ Spark structured streaming: readStream...
├─ DuckDB: SELECT count(*) FROM orders_created
└─ Operational consumer: продолжает читать топик
Ключевая идея: топик и таблица — это один источник правды. Не копия данных, а то же данное в двух представлениях.
Архитектура: три способа реализации
Реализовать Streaming Lakehouse можно тремя путями. Выбор зависит от того, где живут ваши данные (Confluent Cloud / самохост) и какую гарантию вы хотите получить.
Producers
Producers — операционные сервисы (order-service, payment-service). Пишут события в Kafka с обычной producer API. НЕ ЗНАЮТ о существовании Lakehouse-материализации — это полностью прозрачно.Kafka
Kafka cluster. Topics: orders.created, payments.charged. Schema Registry с Avro/Protobuf. Retention 7 дней — Lakehouse даёт permanent storage, не нужно держать долго в Kafka.Iceberg / Delta
Iceberg / Delta tables. Storage: S3 / GCS / ADLS. Format: Parquet файлы + JSON manifest. Catalog: Apache Polaris / Lakekeeper / Unity / Nessie / AWS Glue. Snapshot-based: каждый commit создаёт новый immutable snapshot — time travel из коробки.Способ 1: Iceberg Sink Connector
Apache Iceberg Sink Connector (Apache 2.0, codebase в apache/iceberg) — community-maintained sink для Kafka Connect. Работает в любом self-managed Kafka Connect кластере.
connector.class=org.apache.iceberg.connect.IcebergSinkConnector
topics=orders.created,orders.cancelled,payments.charged
iceberg.catalog=rest
iceberg.catalog.uri=https://polaris.example.com/api/catalog
iceberg.tables.dynamic-enabled=true
iceberg.tables.route-field=topic
iceberg.tables.auto-create-enabled=true
iceberg.tables.evolve-schema-enabled=true
iceberg.control.commit.interval-ms=300000
Гарантии: exactly-once через двухфазный commit (Kafka offsets + Iceberg snapshot — одна транзакция). Schema evolution: автоматический — добавление колонок из Avro-схемы. Multi-table fan-out: одна тема → несколько Iceberg-таблиц по полю в payload.
Способ 2: Confluent Tableflow
Tableflow — managed-функция Confluent Cloud (GA в 2025). В одном клике любой топик объявляется Iceberg или Delta таблицей. Никакого Kafka Connect — Confluent делает всё под капотом.
Что Tableflow делает автоматически:
- Schematization: конвертация Avro/Protobuf в Iceberg schema.
- Type conversions: Avro
logicalType: timestamp-micros→ Icebergtimestamp. - Schema evolution: добавление новых колонок из Schema Registry.
- CDC stream materialization: для топиков с Debezium events применяется upsert семантика, поддерживается merge-on-read.
- Catalog publishing: регистрация таблицы в AWS Glue, Apache Polaris, Snowflake Horizon, Unity Catalog.
- Table maintenance: периодическая compaction, snapshot expiry, orphan files cleanup.
Roadmap (2026): bidirectional — Iceberg-таблица как источник для Kafka-топика. Изменения в таблице (например, dbt-модель) материализуются обратно в стрим.
Способ 3: Debezium Server / Iceberg Sink (CDC-first)
Debezium Server — standalone-процесс Debezium без Kafka Connect. Читает WAL базы данных и пишет change events напрямую в указанный sink (включая Iceberg). Полезно когда:
- Источник — реляционная БД (Postgres, MySQL).
- Нужен outbox-паттерн с Lakehouse как destination для аналитики.
- Не хочется содержать отдельный Kafka Connect кластер.
debezium.sink.type=iceberg
debezium.sink.iceberg.catalog-name=rest
debezium.sink.iceberg.warehouse=s3://lakehouse/warehouse
debezium.sink.iceberg.table-namespace=cdc_events
debezium.source.connector.class=io.debezium.connector.postgresql.PostgresConnector
В каноническом сценарии Debezium Server + Iceberg + REST catalog даёт полный CDC pipeline без Kafka в роли промежуточного storage.
Apache Paimon vs Iceberg streaming write
Apache Paimon (выпущенный из Alibaba как Flink Table Store, перешёл в Apache top-level в 2024) — streaming-first табличный формат. Iceberg V3 (2026) добавил streaming-write capabilities, но архитектурная философия принципиально иная.
| Свойство | Apache Paimon | Apache Iceberg V3 |
|---|---|---|
| Origin | Flink-native, LSM-tree | Batch-first, добавил streaming в V3 |
| Storage layout | LSM-tree (memtable + sorted runs) | Snapshot-based с manifest files |
| Streaming write latency | 1-5 минут (theoretical: секунды) | ≥ 1 час recommended (snapshot commit overhead) |
| Update / upsert | Native через primary key + LSM merge | Merge-on-read (Iceberg V2) или copy-on-write |
| Compaction | Continuous (background, triggered by Flink) | Manual / scheduled job |
| Engine support | Flink (нативный), Spark, Trino | Spark, Flink, Trino, Presto, DuckDB, ClickHouse |
| Use case | Real-time CDC dashboards, IoT, минута-latency аналитика | Большинство analytical workloads, time-travel, schema evolution |
Когда Paimon:
- Источник — Flink job с CDC (например, MySQL → Paimon через Flink CDC connector).
- Требуется sub-minute latency для аналитики.
- Высокая частота upserts (primary key обновляется тысячи раз / сек).
Когда Iceberg:
- Multi-engine аналитика (Spark + Trino + DuckDB + ClickHouse одновременно).
- Schema evolution и time-travel — главные требования.
- Streaming-write — вторичный сценарий, latency несколько минут приемлема.
В production реальных компаний нередко оба формата живут параллельно: Paimon для real-time dashboards, Iceberg для исторической аналитики.
Decision Tree: Streaming Lakehouse vs классический Kafka Connect ETL
Kafka topic → нужна аналитика
Стартовая точка — есть Kafka топик, нужно сделать данные доступными для аналитики (SQL queries, BI dashboards).< 5 min → Streaming Lakehouse
Latency < 5 минут. Нужна streaming-материализация. Tableflow если Confluent Cloud, Iceberg Sink Connector если самохост.5-60 min → batch sink
Latency 5-60 минут. Streaming Lakehouse — overkill. Достаточно классического Kafka Connect S3/JDBC Sink с batch interval 5-15 минут.> 1 hour → batch ETL
Latency > 1 час. Самый простой путь — Airflow / dbt incremental refresh с чтением Kafka через Spark batch.Upserts? → Paimon / Iceberg V2 MoR
Дополнительное измерение: нужны ли upserts (primary key updates)? Если да — Paimon или Iceberg V2 merge-on-read. Если только append-only events — простой Iceberg.Multi-engine? → Iceberg
Multi-engine аналитика? Spark + Trino + DuckDB одновременно — Iceberg. Только Flink — Paimon допустим.CDC source? → Debezium path
Source — реляционная БД с CDC? — Debezium Server + Iceberg Sink. Source — Kafka топик из микросервиса? — Tableflow / Iceberg Sink Connector.Критерии для Streaming Lakehouse:
- Latency аналитики важна (< 5 мин).
- Объём топика большой (> 10K msg/s) — батч-ETL создаст backpressure.
- Schema evolution регулярная — нужна автоматизация.
- Multi-consumer: одни и те же данные нужны и operational consumer, и аналитикам.
Критерии для классического Kafka Connect ETL:
- Latency 30+ мин допустима.
- Простые трансформации, можно отложить до dbt.
- Команда уже умеет эксплуатировать Kafka Connect — переход на Iceberg Sink не даст benefit.
Real-world examples
Netflix
Netflix обрабатывает ~500 миллиардов событий в день (~1.3 PB / day, peak 8M events/sec). Тысячи Kafka топиков, тысячи Flink streaming jobs. Архитектура: events → Kafka → Iceberg (через Flink streaming write + Iceberg sink) → Trino / Spark для ad-hoc и ML. Bronze (raw events), Silver (cleansed), Gold (aggregated) layers — медальная архитектура поверх Iceberg. Lakehouse даёт streaming responsiveness и batch learning на одном фундаменте.
Pinterest публиковал в 2023-2024 inженерные блоги о миграции с lambda-архитектуры (Kafka → Druid для real-time + Kafka → Hive для batch) на единый Iceberg layer. Driver — устранение дублирования данных и single source of truth для аналитики и ML feature stores.
Confluent / Snowflake
Совместная архитектура: Kafka топики → Tableflow → Iceberg в Snowflake Horizon Catalog → Snowflake compute читает Iceberg напрямую (no copy). Маркетируется как zero-ETL streaming аналитика. Реальная ценность — устранение JDBC sink и Snowpipe pipelines.
Sortable
Sortable (ad-tech) — публичный кейс использования Iceberg Sink Connector. CDC из Postgres + bid-events из Kafka → Iceberg на S3 → Trino. Уменьшение data-lag с 1 часа (Airflow + S3 Sink) до 5 минут.
Anti-patterns
Small files problem
Streaming write создаёт много мелких файлов: каждые 1-5 минут commit → новый Parquet файл. Через неделю — десятки тысяч файлов в одной partition. Query performance деградирует катастрофически: Trino сканирует thousands of file headers вместо чтения данных.
Решение: регулярная compaction. Iceberg rewrite_data_files процедура переписывает мелкие файлы в большие (target 128 MB - 1 GB). Запускайте по расписанию (Airflow / dbt) или непрерывно (Tableflow делает автоматически). Если средний размер файла < 10 MB — compaction слишком редкая.
CALL system.rewrite_data_files(
table => 'warehouse.orders_created',
options => map('target-file-size-bytes', '536870912'), -- 512 MB
where => 'created_at > current_date - interval ''7'' day'
);
Schema evolution conflicts
Kafka Schema Registry (BACKWARD compatibility) и Iceberg schema evolution имеют разные правила. Iceberg позволяет добавлять/удалять/переименовывать колонки по ID, Schema Registry — по имени. Если producer добавил колонку discount_pct, а Iceberg sink не настроен на evolve-schema-enabled=true — sink упадёт на первом сообщении с новой схемой.
Решение: включить schema evolution в sink (iceberg.tables.evolve-schema-enabled=true), валидировать compatibility в CI: ваш Avro-схема должна быть compatible и для Schema Registry и для текущей Iceberg-схемы.
Retention mismatch
Kafka retention 7 дней. Iceberg retention “навсегда”. Если sink lag > 7 дней (например, отвалился connector) — данные в Kafka удалятся до того, как попали в Iceberg. Permanent loss.
Решение: мониторинг sink lag, alert при lag > 50% от Kafka retention. Для критичных данных Kafka retention минимум 14-30 дней или включить Tiered Storage.
Snapshot explosion
Streaming write создаёт snapshot каждые 1-5 минут. Через год — десятки тысяч snapshot. Metadata files (manifest list, manifest) разрастаются, query planning замедляется.
Решение: регулярный snapshot expiry (expire_snapshots), оставляйте только последние 7-30 дней snapshot для time-travel. Tableflow делает автоматически.
CDC + Iceberg V1
Iceberg V1 — append-only, не поддерживает row-level deletes. Если ваш source — CDC (UPDATE / DELETE из Postgres), нужен Iceberg V2 с merge-on-read или Apache Paimon с primary key tables. На V1 upserts реализуются как append + последующая dedup-job — медленно и дорого.
Cross-references
- Storage formats (этот курс не покрывает глубоко, но смежный контент в data-engineering направлениях): различия Avro, Protobuf, Parquet, ORC. Для Streaming Lakehouse важно: producers пишут Avro в Kafka, sink конвертирует в Parquet для Iceberg.
- Debezium + Iceberg Sink (Wave 2 контент): полная CDC-цепочка Postgres → Debezium → Kafka → Iceberg Sink Connector → Iceberg на S3.
- Iceberg REST catalogs (sd-data-engineer Wave 2): Apache Polaris, Lakekeeper, Nessie, Unity Catalog OSS — выбор catalog зависит от scale, governance модели и multi-engine требований.
- Transactional Outbox (урок 03 этого модуля): Streaming Lakehouse — естественное продолжение outbox для аналитики. Outbox-таблица → Debezium → Iceberg-таблица events.
- CQRS (урок 02): Iceberg как один из read-models для аналитических запросов.