DuckDB как микро-хранилище: заменяем «ETL + Postgres» одним файлом, одним движком

от автора

Частая история: данные приложения попадают куда-то, джоб их чистит, 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 это выглядит так:

  1. Ежедневный снапшот ключевых таблиц в Parquet.

  2. События как Parquet, разбитый по дате.

  3. Золотые метрики как вьюхи в DuckDB.

  4. Выгрузка curated-таблиц обратно в Parquet для BI-инструментов.

DuckDB читает Parquet напрямую, в том числе несколько файлов как одну логическую таблицу.

Если данные лежат в S3-совместимом объектном хранилище, расширение httpfs поддерживает удалённый доступ к файлам, включая S3 API.


Рабочие сниппеты: джоб обновления и «золотой» слой

1. Минималистичный скрипт обновления (Python)

import duckdbfrom datetime import datetimeDB = "micro_warehouse.duckdb"con = duckdb.connect(DB)# Одноразовая установка (безопасно запускать повторно в большинстве случаев)con.execute("INSTALL httpfs;")con.execute("LOAD httpfs;")# Читаем партиционированный Parquet как логическую таблицуcon.execute("""CREATE OR REPLACE VIEW bronze_events ASSELECT *FROM read_parquet('data/events/date=*/events-*.parquet');""")# Silver: типизация + очисткаcon.execute("""CREATE OR REPLACE VIEW silver_events ASSELECT  CAST(event_time AS TIMESTAMP) AS event_time,  user_id,  team_id,  event_name,  NULLIF(properties, '') AS propertiesFROM bronze_eventsWHERE user_id IS NOT NULL;""")# Gold: метрики (стабильный интерфейс)con.execute("""CREATE OR REPLACE VIEW gold_weekly_active_teams ASSELECT  date_trunc('week', event_time) AS week,  COUNT(DISTINCT team_id) AS weekly_active_teamsFROM silver_eventsGROUP BY 1ORDER BY 1;""")# Экспортируем curated-таблицу для BIcon.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_logSELECT 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.

ссылка на оригинал статьи https://habr.com/ru/articles/1022746/