ADG Оставить заявку
Блог Данные и аналитика 5 мин чтения

Data lakehouse на отечественном стеке: ClickHouse + MinIO + Iceberg и то, что пришлось допиливать

Строим lakehouse на ClickHouse, MinIO и Apache Iceberg для российского корпоратива. Что не работает из коробки и какие патчи нам пришлось писать.

Контекст момента

Концепция data lakehouse утверждается как архитектурный стандарт BI-платформ в корпоративном секторе РФ

Последние полгода мы реализуем lakehouse-архитектуру для нескольких крупных заказчиков - финансы, ритейл, производство. Все хотят примерно одного: единое озеро данных, поверх которого работает BI без выгрузок в отдельный DWH, история изменений, версионирование, возможность делать time-travel запросы. И всё это на отечественном или нейтральном open-source стеке, без Databricks и Snowflake.

Концепция звучит стройно. На практике - значительная часть инструментария интегрируется в связку ClickHouse + MinIO + Iceberg с трением и рядом мест, где документация заканчивается раньше, чем проблема.

Архитектура в двух словах

Схема, к которой мы пришли:

Sources -> Kafka -> Landing (Parquet/Iceberg) in MinIO
                          |
                   ClickHouse (serving layer via Iceberg engine)
                          |
                     BI-инструменты / API

MinIO - object storage, хранит Parquet-файлы организованные через Iceberg table format. Здесь живут все исторические данные, холодный слой, snapshot-ы. Apache Iceberg - table format поверх MinIO: версионирование схемы, partition evolution, time-travel, транзакционные коммиты. ClickHouse - serving layer: читает Iceberg-таблицы напрямую через движок IcebergS3, обеспечивает скорость OLAP-запросов.

Концептуально всё логично. В деталях - куча нюансов, о которых ниже.

Что не работает из коробки

Первое - ClickHouse IcebergS3 и partition pruning. ClickHouse поддерживает чтение Iceberg-таблиц через движок IcebergS3 начиная с версии 24.x, а в 26.1 интеграция заметно подтянулась. Но partition pruning работает не для всех типов партиций Iceberg. Трансформации типа bucket(col, N) или truncate(col, N) - движок их видит в метаданных, но не умеет транслировать в предикаты при сканировании. Результат: запрос с фильтром по партиционированному полю читает все файлы вместо нужного бакета. На больших таблицах это кратная разница во времени ответа.

Временное решение, которое мы применяем: для горячих таблиц используем простые партиции по дате (day(event_time) или month(event_time)) - их ClickHouse прунит нормально. Bucket-партиции оставляем только для холодных таблиц, где запросы через ClickHouse не критичны по скорости.

Второе - catalog и координация записи. Iceberg требует catalog для отслеживания текущего состояния таблицы: кто последний сделал коммит, какой snapshot актуален. Популярные варианты - REST catalog, Hive Metastore, Nessie. Всё это компоненты, которые надо эксплуатировать. На некоторых проектах заказчик хотел обойтись без лишнего сервиса - минимальный стек. Мы пробовали Hadoop-каталог поверх MinIO (просто файл в S3), и столкнулись с проблемой при параллельной записи из нескольких потоков: оптимистичный lock на файл каталога приводил к конфликтам при высокой частоте коммитов. Пришлось внедрить REST catalog (написали тонкую обёртку на Go поверх PostgreSQL) - надёжно, но это ещё один сервис.

Третье - schema evolution и ClickHouse. Iceberg позволяет добавлять, удалять, переименовывать колонки с сохранением обратной совместимости через column IDs. ClickHouse IcebergS3 при изменении схемы требует пересоздания внешней таблицы или вызова REFRESH - автоматического подхвата новых колонок нет. Мы написали небольшой daemon, который подписывается на события каталога и автоматически делает ALTER TABLE ... MODIFY COLUMN в ClickHouse при обнаружении drift-а схемы. Несложно, но это именно тот тип вещей, который не описан в документации - понимаешь, что нужно, когда уже сидишь в проде с рассинхронизированной схемой.

Четвёртое - MinIO и Iceberg compaction. Iceberg по мере работы накапливает мелкие файлы - особенно при частой ingest-нагрузке через Kafka. Compaction (процедура объединения мелких файлов в крупные) обычно делается через Apache Spark или Flink. Нам не хотелось тянуть Spark ради compaction. Решение - Apache Iceberg Java API напрямую через небольшой compaction-сервис, написанный на Java. Работает, запускается по расписанию, но требует внимания к настройке порогов: слишком агрессивный compaction конкурирует с ingest-нагрузкой за ресурсы MinIO.

Что работает хорошо

Справедливости ради: time-travel запросы через ClickHouse через IcebergS3 с явным указанием snapshot_id работают стабильно - это одна из главных причин, по которой мы вообще взялись за Iceberg. Schema evolution в самом Iceberg работает именно так, как обещают: обратная совместимость между старыми и новыми файлами реальная, не декларативная. MinIO в режиме versioning + object lock нормально закрывает требования по неизменяемости данных, которые у корпоративных заказчиков часто прилетают из соображений аудита.

Мы используем ClickHouse 26.1 - там S3-планировщик стал заметно стабильнее, про это писали отдельно. На больших range-сканах это ощутимо.

Где мы сейчас

Один проект из трёх уже в проде - финансовый заказчик с ~3TB активных данных в lakehouse. Два других в стадии пилота. По финансовому проекту можем сказать: стек работает, query-latency для BI-запросов укладывается в требования, ночные ETL-окна стали короче за счёт incremental processing через Iceberg.

Патчи и вспомогательные сервисы, которые нам пришлось написать (catalog wrapper, schema-sync daemon, compaction service), - это примерно три человеко-месяца работы сверх базового внедрения. Это надо учитывать при оценке трудоёмкости. Стек не «разворачивается за день», и тот, кто говорит иначе, скорее всего не дошёл до production нагрузки.

Если задумываетесь над lakehouse-архитектурой для своего BI - в рамках DWH/BI-проектов мы помогаем пройти этот путь с меньшим количеством граблей.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.