DuckLake (DuckDB): «SQL как Lakehouse-формат». Подробный разбор для команды DWH/BI
TL;DR
DuckLake — это новый открытый lakehouse-формат от команды DuckDB: данные лежат в открытых файлах (Parquet) в объектном/файловом хранилище, а все метаданные (каталоги, схемы, снимки/версии, статистики, транзакции) хранятся и управляются обычной SQL-СУБД (PostgreSQL/MySQL/SQLite/DuckDB). Это радикально упрощает архитектуру по сравнению с классическими форматами уровня таблицы (Iceberg/Delta/Hudi), где сложная иерархия JSON/Avro-файлов и каталог-сервис пытаются компенсировать ограничения blob-хранилищ. DuckLake даёт полноценные ACID-транзакции (включая跨-табличные), time-travel, инкрементальные чтения, «тонкие» снапшоты, опциональное инлайнинг-малых изменений и совместимость по данным с Iceberg — при меньшей операционной сложности.
От DWH к Lakehouse — и где «болит» сегодня
Исторически мы прошли путь:
- Data Warehouse — хранение и вычисления на одном сервере/кластере.
- Data Lake — разделение хранения/вычислений, дешёвое S3-подобное хранилище, Parquet как базовый формат.
- Lakehouse — над озером добавили «табличные» возможности: снапшоты, эволюцию схемы, удаление/апдейты, time-travel. Это обеспечивают Iceberg/Delta/Hudi, но ценой сложной файловой мета-структуры и каталога поверх неё.
Проблемы типичного Lakehouse сегодня:
- Непостоянная консистентность в объектных хранилищах — сложно атомарно «переключить» указатель на новую версию таблицы.
- Сложные цепочки метафайлов (JSON/Avro, манифесты, манифест-листы), «шум» мелких файлов, непростые процедуры компакта/вакуумирования.
- Транзакции в рамках одной таблицы есть, но 跨-табличные (multi-table) — трудно, приходится городить внешние оркестрации и «двухфазные» костыли.
Идея DuckLake: «SQL как Lakehouse-формат»
Наблюдение: раз уж для консистентности всё равно «подкладывают» базу (каталог-сервис поверх БД), то почему бы не доверить БД все метаданные целиком? Так и сделано в DuckLake: формат описан как набор реляционных таблиц и чистых SQL-транзакций, описывающих операции со схемами и данными (append/update/delete), включая 跨-табличные ACID-транзакции. При этом данные остаются в Parquet, а метаданные — в SQL-БД.
Ключевые следствия дизайна DuckLake:
- Простота: никакого леса Avro/JSON, никакого отдельного REST-каталога/API — «всё есть SQL». Установка на ноутбук — это DuckDB + расширение ducklake; в продакшене — любая SQL-СУБД с ACID и PK (чаще PostgreSQL).
- Масштабирование: три чётких слоя — storage (S3/GCS/Azure/DFS), compute (клиенты с ducklake), metadata (SQL-БД). Метаданные — на порядки меньше данных, значит центральная БД не узкое место при нормальном тюнинге.
- Скорость: коммит = одна SQL-транзакция по метаданным (после «стейджинга» Parquet). Нет многошаговых HTTP-обращений за манифестами; меньше конфликтов и «гонок». Есть инлайн-режим для малых изменений (запись прямо в каталог-БД), что резко снижает мелкофайловость.
- Богатые фичи «из коробки»: time-travel, снапшоты с низкой стоимостью, transactional DDL, вложенные типы, статистики и прайминг/прюнинг, 跨-табличные транзакции, SQL-views, инкрементальные сканы (change data feed).
- Совместимость с Iceberg-данными: DuckLake пишет данные и positional-delete файлы совместимые с Iceberg, что даёт путь метаданных-only миграции (без перепаковки данных).
По духу это похоже на подход BigQuery (с Spanner) и Snowflake (с FoundationDB) — БД как источник транзакционной истины для метаданных, только с открытыми форматами файлов внизу.
Архитектура и развёртывание
Компоненты:
- Catalog DB (PostgreSQL/MySQL/SQLite/DuckDB) — хранит все таблицы DuckLake-метаданных, снапшоты/статистики, настройки. Рекомендуем PostgreSQL для многопользовательской работы. Если использовать DuckDB как каталог, это один клиент одновременно (для тестов/локалки ок).
- Storage — Parquet в S3/GCS/Azure Blob/локальном NAS. Путь задаётся при создании каталога/схемы/таблицы; с версии 0.2 добавлены относительные пути на уровнях схема/таблица (удобно для префиксных прав доступа).
- Compute-клиенты — движки, понимающие DuckLake. Сегодня есть полноценное расширение DuckDB ducklake (MIT, open-source).
Быстрый старт (hands-on)
Локально (каталог в файле DuckDB, один клиент)
INSTALL ducklake; -- ставим расширение
ATTACH 'ducklake:metadata.ducklake' AS lake (DATA_PATH 'data_files/');
USE lake;
CREATE TABLE main.sales (id BIGINT, amount DOUBLE, ts TIMESTAMP);
INSERT INTO main.sales VALUES (1, 100.5, now()), (2, 42.0, now());
-- Time Travel по версии снапшота:
FROM main.sales AT (VERSION => 1);
-- Инкрементальные изменения между версиями:
FROM ducklake_table_changes('lake', 'main', 'sales', 1, 2);
Продакшен-вариант (каталог = PostgreSQL, данные = S3)
INSTALL ducklake;
INSTALL httpfs; -- для S3 в DuckDB
LOAD ducklake;
LOAD httpfs;
-- Настройки доступа к S3 задаются через DuckDB Secrets/PRAGMA (см. доки httpfs),
-- далее прикрепляем DuckLake-каталог в PostgreSQL:
ATTACH 'ducklake:postgres:dbname=ducklake_catalog host=pg-host user=... password=...'
AS lake (DATA_PATH 's3://my-bucket/datalake/');
USE lake;
CREATE SCHEMA bronze;
CREATE TABLE bronze.events (
event_id BIGINT, payload JSON, event_ts TIMESTAMP
) PARTITION BY (year(event_ts), month(event_ts)); -- v0.2: partition transforms
-- Запись батча:
INSERT INTO bronze.events SELECT * FROM read_parquet('s3://ingest/events_2025_08.parquet');
-- Инкрементальные вставки между снимками:
FROM ducklake_table_insertions('lake', 'bronze', 'events', 120, 130);
-- Обслуживание: чистка старых файлов/снапшотов и мердж смежных:
CALL ducklake_cleanup_old_files('lake', cleanup_all := false, dry_run := true);
CALL ducklake_expire_snapshots('lake', older_than := now() - INTERVAL '30 days');
CALL ducklake_merge_adjacent_files('lake');
Как это работает под капотом (операционная логика)
Коммит изменения в DuckLake = два шага:
- «Staging» — кладём новые Parquet-файлы (если нужны) в storage.
- Один SQL-транзакционный блок в Catalog DB, где:
- регистрируем пути новых файлов,
- обновляем глобальные статистики таблицы/колонок,
- создаём новый снапшот,
- логируем набор изменений (для change-feed).
Это снижает конфликтность, убирает многократные round-trip’ы к blob-хранилищу и ускоряет как чтение, так и коммиты. Для малых изменений работает инлайнинг (запись прямо в каталог-БД), уменьшая количество файлов и повышая скорость до суб-миллисекундных вставок.
План чтения: один запрос к Catalog DB возвращает точный список целевых файлов c учётом схемы, партиционирования и статистик (мин/макс), что даёт ранний pruning и меньше I/O по сети.
Функциональные возможности (что важно для практики)
- ACID跨-табличные транзакции и transactional DDL (создание/эволюция/удаление схем/таблиц/представлений).
- Time-travel и snapshot isolation, «дешёвые» снапшоты; поддержка миллионов снапшотов (снапшот — это «несколько строк» в каталоге).
- Change Data Feed: функции ducklake_table_insertions/deletions/changes. Удобно для инкрементов и CDC-пайплайнов.
- Эволюция схемы (добавить/удалить/переименовать/изменить типы), вложенные типы, views.
- Partition transforms (year/month/day/hour) и scoped settings (глоб./схема/таблица) — с версии 0.2.
- Регистрация уже существующих Parquet-файлов (name-mapping, без перепаковки) — с версии 0.2.
- Шифрование файлов (ключи управляются Catalog DB) и значительно меньше компактов (по сравнению с Iceberg/Delta).
- Открытая лицензия MIT, расширение DuckDB — open-source, статус 0.1/0.2.
Типовые сценарии для вашей команды
- «Многопользовательский DuckDB»: несколько аналитиков/сервисов читают/пишут в общий Lakehouse-набор, compute — локально рядом с потребителем; Catalog DB обеспечивает транзакционность и координацию. (Если Catalog = DuckDB, режим single-client — только для соло-кейсов/прототипов.)
- Bronze/Silver/Gold без тяжёлого каталога: транзакционные инкременты, snapshot-based CDC, быстрый rollback в прошлом состоянии.
- Миграция с Iceberg: переиспользуем физические Parquet/pos-delete файлы, переносим только метаданные (metadata-only).
- Горизонтальный «self-serve»: команды создают свои схемы/таблицы в общем озере, права — префиксно по путям (v0.2).
Сравнение: DuckLake vs Iceberg/Delta (по делу)
|
Критерий |
DuckLake |
Iceberg/Delta |
|---|---|---|
|
Где живут метаданные |
В SQL-БД (реляционные таблицы, транзакции) |
В файлах (JSON/Avro + каталог-сервис поверх БД) |
|
Коммит |
1 SQL-транзакция в Catalog DB (после staging файлов) |
Многошаговые операции с файлами/манифестами |
|
Мелкие изменения |
Inline-режим, мало файлов |
Частые мелкие файлы → компакты/вакуум |
|
Multi-table ACID |
Да (из коробки) |
Нативно нет (нужны внешние координации) |
|
Операционная сложность |
Ниже (нет REST-каталога, «всё — SQL») |
Выше (сложные цепочки метафайлов, отдельные сервисы) |
|
Миграция |
Возможна metadata-only с Iceberg (файлы совместимы) |
Обратно — зависит от поддержки формата |
(суммарно по официальной статье DuckDB и докам DuckLake).
Практические рекомендации по эксплуатации
Выбор Catalog DB
- PostgreSQL по умолчанию (ACID, PK, зрелая экосистема, репликация/бэкапы).
- MySQL — ок, если экспертиза в доме.
- SQLite/DuckDB — прототип/одиночный клиент. (FAQ DuckLake: при Catalog= DuckDB — single-client.)
Тюнинг транзакций
- Короткие коммиты (держать «критический путь» минимальным: staging файлов вне транзакции, затем одна транзакция по метаданным).
- Пул коннекшенов к Catalog DB, контроль конфликтов (ретраи на уровне клиента приемлемы; Postgres тысячами TPS тянет мета-операции).
Управление снапшотами и файлами
- Плановое ducklake_expire_snapshots (например, хранить N дней), ducklake_cleanup_old_files и ducklake_merge_adjacent_files.
- Для прав доступа к объектному хранилищу — v0.2 относительные пути на уровнях схема/таблица → префиксные ACL.
Схема/партиционирование
- Используйте partition transforms (year/month/day/hour) — поддержка с v0.2.
- Включайте статистики/анализ на этапе записи (DuckLake ведёт глобальные и файловые статистики для раннего prunе).
Инкременты/CDC
- Для потребителей: ducklake_table_insertions/deletions/changes между версиями/временем. Это проще, чем дёргать манифесты.
Безопасность
- Шифрование файлов включайте политикой (ключи управляет Catalog DB), храните секреты доступа к S3 в DuckDB Secrets или внешнем менеджере.
Риски и ограничения (честно)
- Зрелость экосистемы: DuckLake свежий (0.1/0.2), основная полноценная реализация — расширение DuckDB. Для «многодвижкового» мира (Spark/Trino/Presto/ETC) нужна поддержка сторонами/коннекторами. Следите за роадмапом.
- Операционный контур Catalog DB: вы меняете «складной каталог» на настоящую СУБД (бэкапы, реплики, мониторинг, HA) — что, впрочем, большинству ИТ-организаций ближе и понятнее.
- Single-client при Catalog= DuckDB — это умышленное ограничение для простоты локальной работы, для мультиклиента берите PostgreSQL/MySQL.
Примерные паттерны для ваших задач
Паттерн «атомарная загрузка факта + дименшна»
- Сгенерировали и залили Parquet-файлы факта/измерения в staging-префикс.
- Одна SQL-транзакция DuckLake: регистрируем файлы дименшна → регистрируем файлы факта → создаём снапшот на весь набор. Внешний потребитель видит либо старую, либо новую согласованную картину.
Паттерн «быстрые маленькие апдейты витрины KPI»
- Используем inline-режим для малых дельт (минутные/секундные апдейты), периодически «выплёскиваем» накопленное в полноценные Parquet-сегменты. Это уменьшает IOPS/малые файлы и держит запросы быстрыми.
Паттерн «инкремент для downstream BI/ETL»
- Консьюмеры забирают ducklake_table_changes(...) между версиями X и Y, применяя только дельту. Не нужно парсить манифесты или поддерживать собственные журналы.
Короткая памятка по миграции с Iceberg
- Данные (Parquet, positional deletes), произведённые DuckLake, совместимы с Iceberg, что облегчает «обратимость» и metadata-only переходы. Стратегия: оставить файлы на месте, перестроить «шапку» метаданных (или импортировать существующие Parquet через name-mapping в v0.2).
Вопрос-ответ (FAQ для команды)
Q: Это «формат таблицы» или «формат озера»?
A: Формат озера + каталог: один мета-слой управляет многими схемами/таблицами и их транзакциями, а не только «одной таблицей».
Q: Нужен ли отдельный каталог-сервис?
A: Нет. Каталог — это сама SQL-БД с открытой схемой; никаких JSON/Avro-деревьев и отдельных REST-слоёв.
Q: Поддерживаются跨-табличные транзакции (ACID)?
A: Да, это одна из ключевых фич дизайна.
Q: Что с многоклиентным доступом?
A: С PostgreSQL/MySQL/SQLite в качестве каталога — параллельно могут работать многие клиенты. Если каталог = DuckDB-файл, то это один клиент (для прототипов).
Q: Как устроен time-travel?
A: Через снапшоты каталога (snapshot isolation) и запросы ... AT (VERSION => N); есть инкрементальные функции для выборки дельт.
Q: Как подключить существующие Parquet?
A: В v0.2 есть name-mapping и ducklake_add_data_files(...), чтобы регистрировать файлы, где нет field-id.
Q: Как чистить «мусор» и ограничивать рост снапшотов?
A: ducklake_expire_snapshots, ducklake_cleanup_old_files, ducklake_merge_adjacent_files.
Q: Лицензия и статус проекта?
A: MIT; открытый код расширения ducklake. Первые версии — 0.1/0.2.
Вывод для архитектора
Если вы сталкиваетесь с болью «табличных» lakehouse-форматов — сложные манифесты, каталог-сервисы, конфликтные коммиты, мелкие файлы и отсутствие простых跨-табличных транзакций — DuckLake даёт понятную альтернативу: спрячьте всю «умную» логику в реляционной БД, где транзакции — норма, а данные оставьте в открытых Parquet. Это снижает сложность, ускоряет коммиты и упрощает эксплуатацию, при этом не привязывая вас к проприетарному хранилищу.




