Что такое DuckDB и почему «SQLite для аналитики»
Когда инженеру данных нужно посчитать агрегаты по сотне миллионов строк, у него обычно два инструмента: либо тяжёлый распределённый движок (Spark, кластер с координатором и воркерами), либо хрупкий скрипт на pandas, который падает с MemoryError на середине датасета. Между этими крайностями долго ничего не было: ничего, что одновременно понимало бы полноценный SQL, считало бы быстрее pandas, не требовало бы сервера и помещалось в один бинарник. Это была реальная, ощутимая пустота в инструментарии инженера данных, и закрыть её было нечем — пока не появился DuckDB.
DuckDB закрывает именно эту нишу. Это in-process аналитическая СУБД — она работает прямо внутри вашего процесса (Python-интерпретатора, R-сессии, CLI), без отдельного сервера и без сетевого соединения. Версия на момент написания курса — DuckDB 1.5.2 (последняя стабильная, вышла 13 апреля 2026), плюс активная LTS-линия 1.4.x «Andium». DuckDB 2.0 запланирован на сентябрь 2026.
В этом уроке мы разберём, что стоит за фразой «SQLite для аналитики» и почему это точное, а не маркетинговое сравнение. Это первый урок курса, и его задача — дать общую картину: что такое DuckDB, для чего он создан и где проходят его границы. Детали — диалект, типы, внутреннее устройство — будут в следующих модулях; здесь важно увидеть инструмент целиком.
Что значит «in-process» и почему это важно
Большинство СУБД, с которыми вы сталкивались — PostgreSQL, MySQL, ClickHouse — устроены по клиент-серверной модели. Есть отдельный процесс-демон, который слушает порт. Ваше приложение подключается к нему по сети (даже если «сеть» — это localhost), отправляет SQL-запрос, получает результат обратно. Это удобно для многопользовательского доступа, но у этого есть цена: сериализация запроса, сетевой round-trip, десериализация результата.
DuckDB устроен иначе. Это библиотека, которую вы линкуете в свой процесс — точно так же, как линкуете numpy или requests. Нет демона. Нет порта. Нет сетевого протокола. Когда вы вызываете duckdb.sql("SELECT ...") из Python, движок СУБД исполняется в том же адресном пространстве, что и ваш Python-код.
У этого три прямых следствия. Первое: нулевая стоимость передачи данных. Если у вас в Python есть pandas DataFrame, DuckDB может прочитать его прямо из памяти, без копирования и сериализации — потому что находится в той же памяти. Второе: нечего администрировать. Нет процесса, который надо запускать, мониторить, перезапускать. Третье: простая модель развёртывания. Установка DuckDB — это pip install duckdb, и всё. Никаких docker-compose, никаких портов, никаких пользователей и паролей.
Эти три следствия — не разные удобства, а грани одного факта: у DuckDB нет процесса, отдельного от вашего приложения. Раз нет отдельного процесса — нечему изолировать от вас данные (отсюда нулевая стоимость передачи), нечего держать запущенным и мониторить (отсюда отсутствие администрирования), нечего разворачивать как сервис (отсюда простота установки). Один архитектурный выбор — in-process — определяет сразу весь характер инструмента. Курс будет возвращаться к этой мысли не раз: очень многое в DuckDB, и хорошее, и его ограничения, вытекает именно из решения быть встраиваемым, а не серверным.
SQLite — но для другого класса задач
Аналогия с SQLite не случайна. SQLite — самая распространённая СУБД в мире: она встроена в каждый телефон, браузер, множество приложений. Её ключевая идея — встраиваемость: база данных как библиотека, файл базы — обычный файл на диске, никакого сервера.
DuckDB берёт ровно эту идею встраиваемости, но применяет её к другому классу нагрузок. SQLite спроектирован для OLTP (Online Transaction Processing): много мелких операций — вставить строку, прочитать одну запись по ключу, обновить поле. Так работает приложение: пользователь нажал кнопку — одна короткая транзакция.
DuckDB спроектирован для OLAP (Online Analytical Processing): мало запросов, но каждый «широкий» — просканировать миллионы строк, посчитать SUM, GROUP BY, соединить несколько таблиц. Так работает аналитика: посчитать выручку за квартал по сегментам.
| Свойство | SQLite | DuckDB |
|---|---|---|
| Класс нагрузки | OLTP (транзакции) | OLAP (аналитика) |
| Хранение данных | Построчное (row-store) | Поколоночное (columnar) |
| Модель встраивания | In-process, single binary | In-process, single binary |
| Типичный запрос | SELECT * WHERE id = ? | SELECT region, SUM(amount) GROUP BY region |
| Движок исполнения | Построчный интерпретатор | Векторизованный |
| Лицензия | Public Domain | MIT |
Главное техническое отличие — способ хранения данных на диске. Это решающий момент, поэтому разберём его подробнее.
Декларативность SQL: «что», а не «как»Прежде чем перейти к раскладке данных, стоит чуть подробнее объяснить, почему OLTP и OLAP — это действительно два разных мира, а не два режима одного. OLTP-нагрузка обслуживает работу приложения в реальном времени: пользователь совершает действие, и система должна за миллисекунды его обработать. Здесь важны короткие операции над отдельными записями и надёжность каждой записи. OLAP-нагрузка обслуживает принятие решений: аналитик или отчёт задаёт вопрос вроде «как менялась выручка по кварталам», и система перемалывает большой массив накопленных данных, чтобы дать ответ. Здесь важна пропускная способность на больших объёмах, а не скорость отдельной мелкой операции. Эти две задачи предъявляют к движку противоположные требования, и потому СУБД исторически разделились на два лагеря. SQLite — выдающийся представитель OLTP-лагеря среди встраиваемых СУБД. До DuckDB во встраиваемом OLAP-лагере фактически зияла пустота — её DuckDB и заполнил.
Почему columnar-хранение бьёт row-store на аналитике
Представьте таблицу заказов: миллион строк, 20 колонок (order_id, customer_id, amount, region, created_at, …). Её надо как-то разложить в линейную память диска. Есть два способа.
Row-store (как SQLite, PostgreSQL): данные хранятся построчно. Сначала все 20 полей первой строки подряд, затем все 20 полей второй строки, и так далее. Это идеально, когда вам нужна вся строка целиком — типичная OLTP-операция.
Columnar-store (как DuckDB, ClickHouse): данные хранятся поколоночно. Сначала все миллион значений колонки order_id подряд, затем все значения customer_id, затем amount. Это идеально, когда вам нужно несколько колонок по всем строкам — типичная OLAP-операция.
Теперь запрос SELECT region, SUM(amount) FROM orders GROUP BY region. Ему нужны ровно две колонки из двадцати.
В row-store движок вынужден прочитать с диска все строки целиком — потому что значения amount физически перемежаются с 19 ненужными полями. Чтобы добраться до миллиона значений amount, диск прочитает в 10 раз больше данных, чем нужно.
В columnar-store движок читает только блок region и блок amount. Остальные 18 колонок на диске не трогаются вообще. Это даёт двукратную, а на широких таблицах десятикратную экономию ввода-вывода — даже без учёта сжатия.
И это ещё не всё. Когда колонка лежит как непрерывный массив однотипных значений, к ней применимы две вещи, недоступные для row-store: эффективное сжатие (соседние значения одной колонки похожи между собой — лучше сжимаются) и векторизованное исполнение (процессор обрабатывает массив значений пачками, используя SIMD-инструкции). Обе эти темы мы разберём «до железа» в отдельных модулях курса.
Стоит пояснить, почему именно однотипный непрерывный массив так хорош для процессора.
Row vs Columnar: сравнение форматов храненияClickHouse: смена ментальных моделей для OLAP Современный процессор быстрее всего работает, когда выполняет одну и ту же операцию над потоком одинаковых данных: он может предсказать, что будет дальше, заранее подгрузить данные в кэш, а инструкции SIMD позволяют одной командой обработать сразу несколько значений. Колонка целых чисел — идеальная пища для такого режима: миллион значений одного типа, лежащих подряд. Строка же из 20 разнотипных полей — наоборот, «рваные» данные, на которых процессор постоянно переключается. Поэтому columnar-раскладка — это не только про экономию ввода-вывода, но и про то, чтобы дать процессору работать в его самом быстром режиме. Это две независимые причины, по которым columnar выигрывает на аналитике, и обе вытекают из одной идеи: хранить вместе то, что вместе обрабатывается.
DuckDB сжимает данные только для persistent-баз (файл на диске). In-memory база данных хранит колонки несжатыми — потому что цель in-memory режима скорость, а не экономия места. Это важная деталь, к которой мы вернёмся в модуле про storage-формат.
Что DuckDB НЕ есть
Чтобы аналогия с SQLite не сбивала с толку, зафиксируем границы. Знать, чем инструмент не является, не менее важно, чем знать его сильные стороны: именно непонимание границ ведёт к неудачным архитектурным решениям. Разберём четыре частых заблуждения.
DuckDB не сервер. Нельзя поднять его как демон и подключать к нему сотню клиентов по сети. Один процесс — одна база. Если нужен многопользовательский сетевой доступ к одним и тем же данным, DuckDB не ваш инструмент (для облачного сценария есть MotherDuck, но это отдельный managed-сервис).
DuckDB не замена PostgreSQL для приложения. Транзакционная нагрузка веб-приложения — тысячи мелких конкурентных вставок и обновлений в секунду — это анти-паттерн для DuckDB. У него модель конкурентности «один писатель, много читателей» в рамках одного процесса. Для OLTP берите PostgreSQL или SQLite.
DuckDB не «просто быстрый pandas». Это полноценная СУБД: с ACID-транзакциями, реальным оптимизатором запросов, persistent-хранилищем и способностью обрабатывать данные больше объёма RAM (спиллинг на диск). pandas всего этого не умеет.
DuckDB не ограничен размером оперативной памяти. Это частое заблуждение. DuckDB умеет out-of-core исполнение: если датасет не помещается в RAM, движок выгружает промежуточные результаты на диск и доводит запрос до конца. Подробно — в модуле про larger-than-memory.
Где DuckDB особенно уместен
Зная границы, посмотрим на сильную сторону — на задачи, под которые DuckDB создан и где он один из лучших инструментов.
Разведочный анализ данных. У вас есть датасет — файл или несколько файлов — и нужно его понять: посчитать распределения, агрегаты, найти аномалии. DuckDB позволяет делать это интерактивно, полноценным SQL, быстро, без поднятия инфраструктуры. Открыли — и сразу работаете с данными.
Трансформация данных в пайплайне. В подходе ELT (Extract-Load-Transform) данные сначала загружают, а потом трансформируют. DuckDB — отличный движок для шага трансформации на одной машине: читает сырые данные, чистит, агрегирует, моделирует, отдаёт результат. Аналитический SQL для этого — естественный язык.
Аналитика внутри приложения. Приложению нужно посчитать отчёт по локальным данным — DuckDB встраивается в него как библиотека и делает это быстро, не требуя отдельной СУБД-инфраструктуры.
Замена тяжёлым скриптам обработки данных. Скрипт, который медленно перемалывает данные построчно или упирается в память, часто можно заменить на несколько SQL-запросов DuckDB — короче, быстрее и без риска MemoryError.
Общее у этих сценариев — аналитическая нагрузка, одна машина, один процесс. Это и есть профиль DuckDB. Курс дальше разбирает каждый такой сценарий подробно; пока важно увидеть очертания: DuckDB силён там, где нужен быстрый аналитический SQL без тяжёлой инфраструктуры.
Попробуй сам
Лучший способ почувствовать, что такое DuckDB, — запустить его прямо сейчас. Установите DuckDB и убедитесь, что «in-process» — это буквально не фигура речи. Выполните в терминале:
pip install duckdb
python3 -c "import duckdb; print(duckdb.sql('SELECT 42 AS answer'))"
Вывод будет примерно таким:
┌────────┐
│ answer │
│ int32 │
├────────┤
│ 42 │
└────────┘
Обратите внимание: вы не запускали никакого сервера, не открывали порт, не указывали host. Импорт библиотеки — и SQL уже работает. Теперь сравните: попробуйте вспомнить, сколько шагов нужно, чтобы получить такой же результат из PostgreSQL (установить сервер, запустить демон, создать пользователя, подключиться). Эта разница в количестве шагов и есть практический смысл слов «in-process» и «single binary».
Сделав эту проверку, зафиксируйте для себя главный вывод урока одним предложением: DuckDB — это встраиваемая аналитическая СУБД, аналог SQLite для OLAP-нагрузок, и почти все его свойства — и сильные стороны, и ограничения — вытекают из двух решений: быть in-process и хранить данные поколоночно. С этой опорной мыслью вам будет легче воспринимать остальной курс.
Дальше, в следующем уроке, мы пройдём карту всего курса — от SQL-диалекта до байтов storage-формата — чтобы вы видели весь маршрут заранее.
Как создавался курс
Курс создан при участии Claude (Anthropic) как соавтора: ИИ помогал писать материалы, структурировать темы, генерировать примеры кода и диаграммы. Каждая глава проходила ручную сверку с первоисточниками — спецификациями, документацией, исходным кодом рассматриваемых систем — но гарантировать 100% точность невозможно.
Если вы заметили неточность, опечатку или хотите предложить улучшение — напишите в Telegram-группу курса. Это самый ценный вклад в курс, который вы можете сделать.
Углублённое изучение с Claude
Курс рассчитан на самостоятельное изучение, но любая теория быстрее ложится, если задавать вопросы. Рекомендую держать рядом браузерное расширение Claude (claude.com/download) — оно работает с контентом открытой страницы: выделяете кусок урока и спрашиваете напрямую.
Сценарии, которые особенно хорошо работают для углублённого погружения:
- «Объясни проще» / «дай ещё один пример» — когда формулировка из урока не дошла с первого раза.
- «Покажи, как это устроено на уровне кода / железа» — когда хочется спуститься на слой ниже того, что даёт урок.
- «Как это связано с [другая тема курса]» — когда нужно увязать концепцию с тем, что было раньше.
- «У меня в проекте стек X — как применить?» — когда хочется примерить материал на свой реальный кейс.
Это не замена курсу, а способ ускорить интеграцию материала в вашу картину мира. Если что-то из ответов Claude расходится с уроком — присылайте в Telegram-группу, курс будет уточнён.
Нашли ошибку?
Если заметили неточность, опечатку или хотите предложить улучшение:
Telegram-канал
Подписывайтесь, чтобы узнавать об обновлениях и новых курсах: