Learning Platform
Глоссарий Troubleshooting
Урок 02.05 · 22 мин
Средний
use-casesanti-patternsarchitecturedecision-making

Когда использовать DuckDB и когда НЕ использовать (антипаттерны)

Инструмент полезен ровно настолько, насколько ясны его границы. Молоток — отличный инструмент, но если им закручивать шурупы, виноват не молоток. С DuckDB та же история: на своих задачах он один из лучших инструментов, на чужих — плохой выбор, и проблема не в нём, а в применении. Зрелость инженера измеряется не тем, сколько инструментов он знает, а тем, насколько точно он понимает, где каждый из них уместен, а где нет.

Прошлые уроки дали теорию: in-process архитектура, OLAP против OLTP. Этот урок переводит теорию в практическое решение: даёт чёткие критерии, когда DuckDB — правильный выбор, и разбирает анти-паттерны — ситуации, где его применять не следует, с объяснением почему. Это, возможно, самый практичный урок модуля: именно здесь вы научитесь не делать дорогую архитектурную ошибку.

Важно понимать, откуда берётся неправильное применение DuckDB. Почти всегда — из его же достоинств. DuckDB настолько прост в установке и приятен в работе, что возникает соблазн применить его везде: «он отлично себя показал на аналитике — давайте и приложение на нём построим». Этот соблазн и ведёт к анти-паттернам. Поэтому знать сильные стороны инструмента недостаточно — нужно так же чётко знать его границы, чтобы привлекательность не увела за их пределы. Каждый анти-паттерн ниже разобран с причиной: понимая почему DuckDB там не работает, вы не повторите ошибку даже в незнакомой ситуации.


Когда DuckDB — правильный выбор

Decision matrix: pandas / Polars / PyArrow / DuckDB / Spark Когда Trino, когда Spark SQL, когда хранилище

DuckDB силён, когда совпадают несколько условий. Чем больше совпадает — тем увереннее выбор.

Аналитическая (OLAP) нагрузка. Запросы агрегируют, группируют, соединяют таблицы, сканируют много строк по нескольким колонкам. Это профиль, под который DuckDB создан.

Один процесс — один пользователь данных. Аналитик в ноутбуке, ETL-скрипт в пайплайне, аналитический модуль внутри приложения. Данные обрабатывает один процесс, не нужен сетевой многопользовательский доступ.

Желание избавиться от инфраструктуры. Нет кластера, нет сервера, нет демона для администрирования. pip install duckdb — и движок готов. Для локальной аналитики и встраивания это огромный плюс.

Работа с файлами данных напрямую. Есть Parquet, CSV, JSON — на диске или в object storage. DuckDB запрашивает их на месте, без шага импорта в отдельную СУБД.

Аналитика внутри Python/R. Уже есть pandas/Polars DataFrame, и нужен по ним полноценный SQL — быстрее pandas, с настоящим оптимизатором. Zero-copy интеграция здесь делает DuckDB естественным выбором.

Профиль, под который создан DuckDB
OLAP-нагрузкаАгрегации, GROUP BY, JOIN, сканирование многих строк — аналитика, а не транзакции
Один процессДанные обрабатывает один процесс: ноутбук, ETL-скрипт, модуль приложения. Сетевой multi-user доступ не нужен
Минимум инфраструктурыНет кластера и сервера: pip install и движок готов. Ценно для локальной аналитики и встраивания
Файлы и Python-стекПрямой запрос Parquet/CSV/JSON и zero-copy работа с pandas/Polars DataFrame

Конкретные сценарии-фавориты: разведочный анализ датасета в ноутбуке; трансформационный (T) слой ELT-пайплайна на одной машине; быстрый аналитический модуль внутри приложения, считающий отчёты по локальным данным; обработка набора Parquet-файлов без поднятия Spark; локальная замена тяжёлым pandas-скриптам, которые упираются в память. Общее у всех этих сценариев — задача аналитическая, помещается в одну машину и обрабатывается одним процессом. Если ваша задача похожа на любой из них хотя бы по форме, DuckDB почти наверняка хороший выбор; чем больше совпадений с перечисленными условиями, тем увереннее.


Анти-паттерн 1: операционная БД приложения

Ситуация. Команда хочет взять DuckDB как основную базу веб-приложения: пользователи регистрируются, оформляют заказы, обновляют профили — всё пишется в DuckDB.

Почему это плохо. Это OLTP-нагрузка: тысячи коротких конкурентных операций, многие из них — записи. DuckDB реализует модель «один писатель, много читателей»: persistent-файл в каждый момент открыт на запись только одним процессом, и сам движок оптимизирован под аналитику, а не под высококонкурентную мелкую запись. Веб-приложение с несколькими процессами-воркерами, каждый из которых хочет писать, упрётся в эту модель сразу.

Что брать вместо. PostgreSQL — для серверного приложения. SQLite — для встроенной локальной БД приложения. Оба — OLTP-движки, рассчитанные ровно на эту нагрузку.


Анти-паттерн 2: разделяемая сетевая БД для многих сервисов

Ситуация. Несколько микросервисов на разных машинах должны одновременно читать и писать общие данные. Кто-то предлагает поставить DuckDB «как базу для всех».

Почему это плохо. DuckDB — embedded, in-process СУБД. У неё нет сервера, нет демона, нет сетевого протокола, к ней нельзя «подключиться» по сети с другой машины — подключаться не к чему (об этом был урок про in-process архитектуру). DuckDB живёт внутри одного процесса; межмашинный конкурентный доступ к общим данным — вне его модели.

Что брать вместо. Клиент-серверную СУБД с сетевым доступом: PostgreSQL для транзакционных данных. Если задача аналитическая, но нужен именно сетевой managed-доступ к DuckDB-движку в облаке — существует MotherDuck, отдельный managed-сервис (его курс разбирает в модуле 15). Но «голый» DuckDB на роль сетевой разделяемой БД не годится.

Два анти-паттерна и их причина
Анти-паттерн: операционная БДDuckDB как основная база веб-приложения с конкурентной записью — упирается в модель одного писателя
причина
Модель: один писательPersistent-файл открыт на запись одним процессом; движок не для высококонкурентной мелкой записи
Анти-паттерн: сетевая БД для всехDuckDB как разделяемая БД для микросервисов на разных машинах — невозможно: нет сервера и сетевого протокола
причина
Модель: in-processEmbedded-движок без демона и порта: подключаться по сети не к чему

Анти-паттерн 3: высококонкурентная запись из множества процессов

Ситуация. Двадцать параллельных процессов-воркеров одновременно пишут результаты в один persistent-файл DuckDB.

Почему это плохо. Это частный, но очень распространённый случай нарушения модели «один писатель». Persistent-файл DuckDB может быть открыт на запись только одним процессом одновременно. Двадцать пишущих процессов — это конфликт: они не смогут писать в файл параллельно.

Как делать правильно. Если параллелизм нужен внутри одной задачи — используйте многопоточность внутри одного процесса DuckDB: один процесс, много потоков, движок сам распараллелит работу (morsel-driven parallelism, модуль 08). Если данные приходят из множества независимых процессов — пусть каждый пишет в свой отдельный файл (например, свой Parquet), а потом один процесс DuckDB читает их все вместе через glob. Конкурентная запись в один файл из многих процессов — не та модель.

WARNING

Различайте конкурентность потоков и конкурентность процессов. DuckDB отлично параллелит работу между потоками одного процесса — это его сильная сторона. Чего он не делает — это конкурентная запись в один файл из нескольких процессов. «Один писатель» означает один пишущий процесс, а не один поток.


Анти-паттерн 4: распределённая обработка на кластере машин

Ситуация. Датасет — десятки терабайт, и хочется обрабатывать его, распределив по кластеру из десятков машин, как это делает Spark.

Почему это плохо. DuckDB — single-node движок. Он рассчитан на одну машину и прекрасно использует все её ядра и память, умеет out-of-core (данные больше RAM спиллятся на диск — модуль 13). Но он не распределённый: не умеет раскидать один запрос на кластер машин и собрать результат. Для по-настоящему распределённой обработки на кластере DuckDB не предназначен.

Когда это всё-таки не анти-паттерн. Важная оговорка: «больше RAM» — это не «нужен кластер». Современная одна машина имеет сотни гигабайт памяти, а DuckDB ещё и спиллит на диск. Датасеты в сотни гигабайт и единицы терабайт одна машина с DuckDB обрабатывает уверенно — и это часто проще и дешевле кластера. Кластер действительно нужен лишь когда данные не помещаются и в это, либо когда требуется горизонтальное масштабирование под нагрузку. Не спешите тянуть Spark: сначала проверьте, не справится ли одна машина.

Этот пункт заслуживает развёрнутого пояснения, потому что «у нас много данных, значит нужен кластер» — одно из самых дорогих заблуждений в инженерии данных. Кластер — это не бесплатное «больше мощности». Это координатор и воркеры, которые надо развернуть и поддерживать; это сеть между узлами, по которой данные приходится перегонять (а перегонка данных по сети часто оказывается узким местом распределённой обработки); это сложность отладки, когда запрос ведёт себя по-разному на разных узлах. Кластер оправдан, когда задача действительно его требует — но он добавляет постоянную операционную и денежную стоимость.

Одна машина с DuckDB во многих случаях оказывается и быстрее, и дешевле именно потому, что у неё нет сетевого слоя между узлами: все ядра работают над общей памятью, данные никуда не перегоняются. А «потолок» одной машины сегодня высок: сотни гигабайт RAM плюс способность DuckDB спиллить на диск означают, что датасеты, которые «на глаз» кажутся требующими кластера, на деле помещаются в одну ноду. Правильный инженерный порядок — снизу вверх: сначала проверить, справится ли одна машина с DuckDB, и переходить к распределённому решению только когда измерения показали, что одной ноды действительно не хватает. Преждевременный переход на кластер — это плата сложностью и деньгами за мощность, которая, возможно, не нужна.


Итоговая таблица решений

СценарийDuckDB?Почему
Разведка датасета в ноутбукеДаOLAP, один процесс, минимум инфраструктуры
Трансформационный (T) слой ELT на одной машинеДаАналитическая обработка, single-node
Запрос Parquet/CSV без импортаДаПрямое чтение файлов — профиль DuckDB
Аналитика по pandas/Polars DataFrameДаZero-copy, полноценный SQL быстрее pandas
Backend веб-приложения (OLTP)НетМодель «один писатель»; берите PostgreSQL/SQLite
Сетевая разделяемая БД для микросервисовНетEmbedded, нет сервера; берите PostgreSQL (или MotherDuck)
Конкурентная запись из 20 процессов в один файлНет«Один писатель»; пишите в отдельные файлы либо потоками
Распределённая обработка на кластереНетSingle-node; для кластера — Spark/Trino
Датасет в сотни ГБ на одной мощной машинеДаSingle-node + out-of-core справляется; кластер не нужен

Закономерность простая. Все «да» — это OLAP-нагрузка в пределах одной машины и одного процесса. Все «нет» — это либо OLTP, либо многопроцессная конкурентная запись, либо распределённая обработка. Запомнив эту закономерность, вы будете принимать решение быстро и правильно.


Алгоритм решения за три вопроса

Чтобы не держать в голове всю таблицу, сведём выбор к трём последовательным вопросам. Они отсеивают анти-паттерны по одному.

Вопрос первый: это аналитическая нагрузка? Запрос агрегирует, группирует, соединяет, сканирует много строк — или это короткие транзакции приложения, точечный доступ по ключу, конкурентная запись? Если нагрузка транзакционная (OLTP) — DuckDB не подходит, ответ найден, дальше не идём. Если аналитическая — переходим ко второму вопросу.

Вопрос второй: нужен ли сетевой многопользовательский доступ к общим данным? Должны ли разные сервисы с разных машин одновременно работать с одними данными по сети? Если да — «голый» DuckDB не подходит (он embedded, без сервера), нужен либо сетевой движок, либо managed-вариант. Если данные обрабатывает один процесс — переходим к третьему вопросу.

Вопрос третий: помещается ли задача в одну машину? Здесь важно не путать «много данных» с «нужен кластер»: учитывайте, что у одной машины сотни ГБ RAM, а DuckDB ещё и спиллит на диск. Если задача реально превышает возможности одной мощной ноды или требует горизонтального масштабирования — нужен распределённый движок. Если одна машина справляется — это сценарий для DuckDB.

Прошёл все три вопроса с ответами «аналитическая / один процесс / помещается в машину» — DuckDB ваш инструмент. Сорвался на любом из трёх — вопрос сам подсказал, какой класс инструмента нужен вместо. Этот короткий алгоритм покрывает практически все реальные развилки и избавляет от необходимости заучивать таблицу.


Попробуй сам

Примените критерии к реальным архитектурным развилкам. Для каждой решите «DuckDB — да или нет» и письменно обоснуйте причину:

  1. SaaS-стартап: нужна основная БД, в которой пользователи создают и редактируют документы, тысячи операций в секунду.
  2. Data-команда: ежедневный ETL читает 80 ГБ Parquet из S3, чистит, агрегирует и кладёт витрину обратно. Всё на одной виртуальной машине.
  3. BI-инструмент: 50 аналитиков из браузера одновременно гоняют разные SQL-запросы по общему набору данных через сеть.
  4. Ноутбук исследователя: датасет 30 ГБ, нужно посчитать корреляции и агрегаты; машина имеет 16 ГБ RAM.
  5. Лог-пайплайн: 100 параллельных воркеров обрабатывают события и должны куда-то складывать результат.

Подсказки к рассуждению: пункт 1 — какой класс нагрузки? Пункт 3 — что мешает «голому» DuckDB при сетевом multi-user доступе? Пункт 4 — мешает ли то, что датасет больше RAM? Пункт 5 — что не так с записью из 100 процессов в один файл и как это переделать. Сверьте свои ответы с итоговой таблицей решений.


Проверка знанийKnowledge check
Почему использовать DuckDB как операционную БД веб-приложения с высококонкурентной записью — это анти-паттерн, и в чём отличие от того, что DuckDB прекрасно параллелит работу между потоками?
ОтветAnswer
Операционная БД веб-приложения несёт OLTP-нагрузку: тысячи коротких конкурентных операций, многие из которых записи, идущие из нескольких процессов-воркеров. DuckDB реализует модель «один писатель, много читателей»: persistent-файл в каждый момент может быть открыт на запись только одним процессом, и сам движок оптимизирован под аналитику, а не под высококонкурентную мелкую запись. Веб-приложение с несколькими пишущими процессами упрётся в эту модель. Поэтому для backend приложения берут OLTP-движки — PostgreSQL для серверного, SQLite для встроенного. При этом важно не путать два вида параллелизма. «Один писатель» ограничивает конкурентность процессов: один файл нельзя писать из многих процессов одновременно. Но внутри одного процесса DuckDB отлично распараллеливает работу между потоками — morsel-driven parallelism задействует все ядра машины для одного запроса. То есть DuckDB параллельный на уровне потоков одного процесса, но не поддерживает конкурентную запись в файл из нескольких процессов. Если параллельные данные приходят из множества процессов, правильное решение — каждому процессу писать в свой отдельный файл, а затем читать их все одним процессом DuckDB через glob.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Какой сценарий является удачным выбором для DuckDB?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6