Частая история: данные приложения попадают куда-то, джоб их чистит, Postgres хранит их «для аналитики» и вдруг вы обслуживаете ETL-пайплайн и базу данных, которая никогда не была рада OLAP-нагрузке. По моему мнению, для большинства команд это лишние сложности.
Главная сила DuckDB не в том, что он быстрый (хотя это правда). Она в том, что он может работать как микро-хранилище: один .duckdb-файл, который ведёт себя как аккуратный аналитический движок, находится рядом с данными и обеспечивает дашборды, аудиты и еженедельные отчёты без платформенного оверхеда.
Концепция микро-хранилища
Классический warehouse-стек предполагает, что вы решаете задачи:
много одновременных пользователей,
общее управление данными,
долгоживущие пайплайны,
несколько доменов, питающих одну каноническую модель.
Микро-хранилище предполагает нечто меньшее и более честное:
одна команда владеет им,
несколько «золотых» таблиц и вьюх имеют значение,
свежесть данных важнее совершенства,
реальный продукт — надёжные ответы.
DuckDB идеально вписывается во второй мир, потому что это встроенная аналитическая база данных, которая умеет читать локальные файлы (Parquet/CSV/JSON), удалённые объектные хранилища и даже другие базы данных — без превращения стека в распределённый научный проект.
Почему «ETL + Postgres» ломается (и тихо сжигает недели)
Postgres — отличный инструмент. Но его использование как основного аналитического движка превращается в смерть от тысячи мелких порезов.
OLAP-проблемы накапливаются как «мелочи»
Добавляешь индексы, потом ещё, потом vacuum становится ритуалом. Запросы недельных когорт конкурируют с транзакционной нагрузкой. Все начинают просить преагрегированные таблицы, потому что «дашборд тормозит». ETL-джобы сцепляются с правками схемы и разовыми фиксами.
Ничего из этого не взрывается в первый день. Это просто постепенно превращает команду в штатных администраторов базы данных.
Предложение DuckDB прямолинейно: перестань строить мини-платформу данных, когда тебе был нужен просто чистый аналитический слой.
Архитектура: один файл, один движок, меньше точек отказа
Вот форма микро-хранилища, которая работает в реальных командах:
[БД приложения / события] [Файлы в S3/GCS] [CSV вручную] | | | | (разовая выгрузка) | (запрос напрямую) | (разовая загрузка) v v v (снапшот по расписанию) внешние таблицы/вьюхи staging-таблицы \ / / \ / / v v v +---------------------------+ | DuckDB-файл | | bronze -> silver -> gold | +---------------------------+ | | (экспорты, BI, API) v дашборды / отчёты
DuckDB становится «истинным» аналитическим слоем, но система остаётся лёгкой. «Пайплайн» обычно это:
скрипт обновления по расписанию,
несколько вьюх,
воспроизводимый экспорт.
И да, с реальным SLA.
Неожиданно чистый SLA, который можно предложить
Шаблон практического SLA
Свежесть: «Данные обновляются каждые 15 минут (или раз в час), временная метка последнего успешного обновления логируется.»
Корректность: «Золотые метрики определены в версионируемых SQL-вьюхах; изменения проходят ревью.»
Производительность: «Дашборды p95 < 2с для стандартных фильтров за последние 90 дней.»
Восстановление: «При сбое обновления можно пересобрать из Parquet-снапшотов менее чем за 30 минут.»
Стоимость/ops: «Нет постоянно работающего хранилища. Один джоб по расписанию плюс один файл.»
Вот где важен «один файл»: ваше хранилище — не кластер, который вы дебажите в 2 ночи. Это артефакт, который можно бэкапить, копировать и воспроизводить.
Реальный паттерн: продуктовая аналитика без платформенного налога
Типичная ситуация — B2B SaaS-команда:
нужны еженедельно активные команды, adoption фич, когорты удержания,
события в реляционной БД, логи в объектном хранилище,
«хранилище» сейчас — ETL + Postgres + дашборды,
команда маленькая, вопросы стабильные, стек тяжёлый.
С DuckDB это выглядит так:
Ежедневный снапшот ключевых таблиц в Parquet.
События как Parquet, разбитый по дате.
Золотые метрики как вьюхи в DuckDB.
Выгрузка curated-таблиц обратно в Parquet для BI-инструментов.
DuckDB читает Parquet напрямую, в том числе несколько файлов как одну логическую таблицу.
Если данные лежат в S3-совместимом объектном хранилище, расширение httpfs поддерживает удалённый доступ к файлам, включая S3 API.
Рабочие сниппеты: джоб обновления и «золотой» слой
1. Минималистичный скрипт обновления (Python)
import duckdb from datetime import datetime DB = "micro_warehouse.duckdb" con = duckdb.connect(DB) # Одноразовая установка (безопасно запускать повторно в большинстве случаев) con.execute("INSTALL httpfs;") con.execute("LOAD httpfs;") # Читаем партиционированный Parquet как логическую таблицу con.execute(""" CREATE OR REPLACE VIEW bronze_events AS SELECT * FROM read_parquet('data/events/date=*/events-*.parquet'); """) # Silver: типизация + очистка con.execute(""" CREATE OR REPLACE VIEW silver_events AS SELECT CAST(event_time AS TIMESTAMP) AS event_time, user_id, team_id, event_name, NULLIF(properties, '') AS properties FROM bronze_events WHERE user_id IS NOT NULL; """) # Gold: метрики (стабильный интерфейс) con.execute(""" CREATE OR REPLACE VIEW gold_weekly_active_teams AS SELECT date_trunc('week', event_time) AS week, COUNT(DISTINCT team_id) AS weekly_active_teams FROM silver_events GROUP BY 1 ORDER BY 1; """) # Экспортируем curated-таблицу для BI con.execute(""" COPY (SELECT * FROM gold_weekly_active_teams) TO 'exports/gold_weekly_active_teams.parquet' (FORMAT PARQUET); """) con.execute("CREATE OR REPLACE TABLE ops_refresh_log AS SELECT 1 AS dummy;") con.execute(""" INSERT INTO ops_refresh_log SELECT 1 """) print("Обновление OK:", datetime.utcnow().isoformat(), "DB:", DB)
Это не «платформа данных». Это надёжная процедура.
2. Инкрементальные обновления, когда они нужны
Если «золотые» таблицы нужно поддерживать инкрементально, DuckDB поддерживает MERGE — удобно для upsert в материализованную таблицу по ключу.
Схема:
добавляем новые сырые события,
пересчитываем небольшое скользящее окно,
MERGEв материализованную золотую таблицу по ключу(week, team_id).
Приём для миграции: оставить Postgres, но перестать злоупотреблять им
Иногда самая быстрая миграция — не жёсткое переключение, а постепенный сдвиг:
OLTP остаётся в Postgres,
OLAP-запросы переезжают в DuckDB.
У DuckDB есть расширение для Postgres, которое умеет читать и писать в работающий Postgres-инстанс — можно федерировать или мигрировать постепенно вместо переписывания всего за один уик-энд.
Для команд, которым нужны результаты в текущем спринте, это существенно.
Ограничения: когда DuckDB не подходит как хранилище
Микро-хранилище — не универсальный инструмент. Важно честно обозначить границы.
DuckDB подходит, когда:
аналитических пользователей немного (маленькая команда, запросы по расписанию, BI-экспорты),
данные преимущественно append-only или снапшот-based,
вы работаете с Parquet и файловыми воркфлоу,
нужны воспроизводимость и портируемость.
DuckDB не подходит, когда:
нужна высокая конкурентность с большим числом интерактивных пользователей,
нужны тяжёлые OLTP-записи и строгая транзакционная семантика для приложений,
требуется сложное управление данными с политиками на уровне строк для множества арендаторов,
«хранилище» — это контракт компании с десятками продюсеров.
Если вы в первом случае — вам нужна адекватность стека. Если во втором — полноценное хранилище.
Итого: меньший стек, который всё равно ощущается «настоящим»
Главное в подходе микро-хранилища — психологическое: система снова ощущается управляемой.
Один файл. Один движок. Джоб обновления, в котором можно разобраться. Метрики, определённые в SQL, который можно проревьюить. Экспорты, которые можно воспроизвести. SLA, который можно сформулировать без расплывчатых объяснений.
Если сейчас вы запускаете «ETL + Postgres» главным образом потому что это дефолт — возможно, один DuckDB-файл отделяет вас от более простой жизни.
Подписывайтесь на наш Telegram-канал. Мы делаем тренажёры и симуляторы по построению хранилищ на dbt, пайплайнов на Dagster и построению Lakehouse на Spark + Iceberg.