DuckDB — встраиваемая аналитическая СУБД: она работает внутри процесса, хранит данные в одном файле и не требует отдельного сервера. Для отчётов по CSV, Parquet и выгрузкам из продакшена это удобнее, чем поднимать PostgreSQL или считать всё в pandas. При этом SQL настоящий, с оконными функциями, CTE и типами. Ниже — установка, запросы к файлам, экспорт, настройка памяти и честные ограничения.
Место DuckDB среди СУБД
DuckDB — колоночная OLAP-система с векторным исполнением. Она оптимизирована под сканирование больших диапазонов и агрегаты, а не под множество мелких транзакций. SQLite — построчная OLTP-база, PostgreSQL — сервер для параллельных клиентов. DuckDB занимает нишу между ними: аналитика на одной машине, без администрирования, с полноценным SQL. Файл базы можно передать коллеге, положить в архив или открыть только для чтения. Это же свойство делает её удобной для ноутбуков, CI и исследовательских задач.
Установка
Самый короткий путь — Python-пакет. Он содержит и библиотеку, и CLI.
python3 -m venv .venv
source .venv/bin/activate
pip install duckdb
python -c "import duckdb; print(duckdb.__version__)"
Для сервера без Python удобен официальный бинарник CLI. Он не требует зависимостей и ставится в /usr/local/bin. Архив в примере собран под x86-64, для ARM публикуется отдельный.
curl -L -o duckdb.zip https://github.com/duckdb/duckdb/releases/latest/download/duckdb_cli-linux-amd64.zip
unzip duckdb.zip
sudo install duckdb /usr/local/bin/
duckdb --version
Пакеты есть в conda и в репозиториях некоторых дистрибутивов, но версия там отстаёт. Для новых функций лучше официальная сборка.
Файловая база и первые запросы
База — обычный файл. При первом открытии он создаётся, при повторном — открывается.
duckdb analytics.duckdb
CREATE TABLE events (ts TIMESTAMP, user_id BIGINT, action VARCHAR, amount DECIMAL(10,2));
COPY events FROM 'events.csv' (HEADER, DELIMITER ',');
SELECT action, count(*) AS n FROM events GROUP BY ALL ORDER BY n DESC;
GROUP BY ALL избавляет от перечисления колонок. Типы DECIMAL и TIMESTAMP поддерживаются полноценно, в отличие от многих легковесных движков. Команда DESCRIBE events покажет схему, а SUMMARIZE events — распределение значений и долю NULL. Для загрузки больших файлов полезен прогресс-бар и параметр SAMPLE_SIZE при определении схемы.
CSV и Parquet без загрузки
Читать можно напрямую из файлов: DuckDB сама определит схему, а для Parquet возьмёт метаданные.
SELECT * FROM read_csv_auto('logs/*.csv', union_by_name=true) LIMIT 5;
SELECT date_trunc('day', ts) AS day, sum(amount) AS total
FROM read_parquet('exports/2026/*.parquet')
GROUP BY ALL ORDER BY day;
Шаблоны glob раскрываются, партиционирование Hive подхватывается флагом hive_partitioning=true. Проекция и фильтры проталкиваются в сканирование: если выбрать две колонки, остальные не читаются. Для CSV это экономит меньше, для Parquet — заметно. Сжатые gzip-файлы читаются, но параллелизм на них ограничен.
Оконные функции и PIVOT
Аналитика редко обходится без окон. QUALIFY фильтрует результат оконной функции без подзапроса.
SELECT user_id, ts, amount,
sum(amount) OVER (PARTITION BY user_id ORDER BY ts) AS running
FROM events
QUALIFY row_number() OVER (PARTITION BY user_id ORDER BY ts DESC) = 1;
PIVOT разворачивает строки в колонки, UNNEST разбирает списки, а типы LIST и STRUCT позволяют хранить вложенные данные без JSON-хаков. Всё это работает и в CLI, и через Python API. Для разворота достаточно указать колонку, значения и агрегат:
PIVOT events ON action USING sum(amount) GROUP BY user_id;
Экспорт и обмен
Результат удобно отдавать в Parquet: колонки, сжатие, схема.
COPY (SELECT * FROM events) TO 'events.parquet' (FORMAT parquet, COMPRESSION zstd);
EXPORT DATABASE 'backup' (FORMAT parquet);
ATTACH 'other.duckdb' AS other;
INSERT INTO other.events SELECT * FROM events;
EXPORT DATABASE создаёт каталог со схемой и данными, пригодный для переноса. ATTACH открывает вторую базу в той же сессии, поэтому JOIN между файлами не требует промежуточной загрузки. Обратная операция — IMPORT DATABASE — восстанавливает объекты из такого каталога.
Python и Arrow
В Python DuckDB возвращает pandas, Arrow или обычные кортежи.
import duckdb
con = duckdb.connect("analytics.duckdb", read_only=True)
df = con.execute("SELECT action, sum(amount) AS total FROM events GROUP BY 1").df()
read_only не даёт случайно изменить файл и позволяет открыть его параллельно с другим процессом на чтение. Метод .arrow() возвращает таблицу Arrow без копирования, что важно при больших выгрузках. Соединение можно переиспользовать в пределах процесса, но запись всё равно остаётся однопоточной.
Память и производительность
По умолчанию DuckDB берёт разумную долю памяти, но на сервере лучше задать лимиты явно.
PRAGMA threads=4;
PRAGMA memory_limit='4GB';
PRAGMA temp_directory='/var/tmp/duckdb';
EXPLAIN ANALYZE SELECT action, count(*) FROM events GROUP BY ALL;
При нехватке памяти движок сбрасывает промежуточные результаты на диск. EXPLAIN ANALYZE показывает реальное время и число строк, а PRAGMA database_size — размер файла и свободное место. После массовых вставок полезен CHECKPOINT, который сливает журнал в основной файл.
Ограничения
DuckDB — не замена серверной СУБД. Писатель в файл один: два процесса на запись конфликтуют. Нет многопользовательской модели прав и сетевого протокола. Транзакции есть, но конкурентная нагрузка из десятков клиентов — не её сценарий. Для потоковой вставки лучше буферизовать данные и писать пакетами, а для совместного доступа — экспортировать Parquet или использовать серверную базу. Отдельного сервера и демона нет: база открывается процессом, а блокировка файла не даёт двум писателям испортить данные. Это осознанный компромисс ради простоты. Для больших проектов удобно держать SQL в отдельных файлах и запускать их через .read в CLI или execute в Python.
Чек-лист
- Версия DuckDB зафиксирована в зависимостях проекта.
- Файл базы не лежит в сетевой папке с блокировками.
- Для повторяющихся отчётов сохранены SQL-файлы и схема.
- Лимиты памяти и threads заданы для сервера.
- Крупные выгрузки идут в Parquet, а не в CSV.
- Резервная копия — копия файла или EXPORT DATABASE.
Итог
DuckDB закрывает разрыв между скриптом и полноценной аналитической базой. Она ставится за минуту, читает CSV и Parquet без предварительной загрузки, поддерживает оконные функции и нормальные типы. Для интерактивной аналитики на ноутбуке или сервере это часто лучший инструмент. Но если несколько пользователей пишут одновременно или нужны права доступа, выбирайте PostgreSQL или другую серверную СУБД.