DuckDB — встраиваемая аналитическая база данных (OLAP), которая выполняет аналитические запросы прямо внутри вашего приложения или скрипта. Её часто называют «SQLite для аналитики», и сравнение точное: один файл, без сервера и настройки, но оптимизация не под транзакционную запись, а под чтение и агрегацию больших объёмов данных.
Проект вырос в CWI — нидерландском национальном исследовательском институте математики и информатики, написан на C++ и распространяется под лицензией MIT. Сейчас DuckDB — один из стандартных инструментов для аналитики на Python, и его всё чаще встраивают в бэкенды.
OLAP vs OLTP: в чём принципиальное отличие
OLTP (Online Transaction Processing) — транзакционные базы: PostgreSQL, MySQL, SQLite. Оптимизированы для быстрой записи и чтения отдельных строк. Типичный запрос: SELECT * FROM users WHERE id = 12345. Хранение по строкам (row-oriented).
OLAP (Online Analytical Processing) — аналитические базы: DuckDB, ClickHouse, BigQuery, Snowflake. Оптимизированы для запросов, которые читают много строк, но мало колонок, и считают агрегаты. Типичный запрос: SELECT date, SUM(revenue) FROM orders GROUP BY date. Хранение по колонкам (column-oriented).
Почему колоночное хранение быстрее для аналитики? Для SELECT SUM(revenue) FROM orders база читает только колонку revenue, а не все колонки каждой строки. Если в таблице 20 колонок, с диска читается лишь небольшая часть данных. К тому же однотипные значения в колонке хорошо сжимаются.
Как работает DuckDB
Как и SQLite, DuckDB работает как библиотека внутри вашего процесса. Данные хранятся в файле .duckdb или в памяти для временных вычислений.
Установка и первые запросы
# Python
pip install duckdb
# Node.js (официальный клиент)
npm install @duckdb/node-api
# CLI
brew install duckdb # macOS
winget install DuckDB.cli # Windows
import duckdb
# Файловая база
con = duckdb.connect('analytics.duckdb')
# Или в памяти
# con = duckdb.connect(':memory:')
con.execute("""
CREATE TABLE sales (
date DATE,
product_id INTEGER,
category VARCHAR,
revenue DECIMAL(10,2),
quantity INTEGER,
region VARCHAR
)
""")
# Загрузка из CSV
con.execute("COPY sales FROM 'sales_2026.csv' (FORMAT CSV, HEADER true)")
result = con.execute("""
SELECT
date_trunc('month', date) AS month,
category,
SUM(revenue) AS total_revenue,
AVG(revenue) AS avg_revenue,
COUNT(*) AS order_count
FROM sales
WHERE date >= '2026-01-01'
GROUP BY 1, 2
ORDER BY 1 DESC, 3 DESC
""").df() # .df() возвращает pandas DataFrame
print(result)
Запросы к файлам без загрузки
DuckDB выполняет SQL прямо по CSV, Parquet и JSON-файлам:
import duckdb
import pandas as pd
# CSV напрямую
result = duckdb.sql("""
SELECT region, SUM(revenue) AS total, COUNT(*) AS orders
FROM read_csv('transactions_2026.csv')
GROUP BY region
ORDER BY total DESC
LIMIT 10
""").df()
# Parquet в S3 (нужен httpfs и секрет с ключами, см. ниже)
result = duckdb.sql("""
SELECT product_id, SUM(quantity) AS sold
FROM read_parquet('s3://data-lake/sales/*.parquet')
WHERE date >= '2026-01-01'
GROUP BY 1
""").df()
# pandas DataFrame как таблица
df = pd.read_csv('data.csv')
result = duckdb.sql("SELECT * FROM df WHERE revenue > 1000").df()
Параллельное выполнение
DuckDB по умолчанию использует все ядра процессора и обрабатывает данные векторами, а не по одной строке. Поэтому GROUP BY и агрегации по большим таблицам в нём обычно заметно быстрее, чем в строковых OLTP-базах на той же машине. Насколько быстрее — зависит от данных, запроса и железа, так что меряйте на своей задаче.
Производительность: как сравнивать честно
Мы сознательно не приводим таблицу «секунд против секунд»: результаты сильно зависят от версии, железа, формата данных и того, прогрет ли кэш. Если нужны цифры, смотрите открытые воспроизводимые бенчмарки (например, ClickBench) и запускайте их на своём оборудовании. Общая картина устойчива:
- на сканах, агрегациях и
JOINбольших таблиц DuckDB и ClickHouse сильно опережают PostgreSQL и SQLite; - на точечных
SELECTпо ключу и вставке отдельных строк быстрее OLTP-базы; - ClickHouse масштабируется на кластер, DuckDB работает на одной машине.
Экосистема расширений
httpfs — чтение файлов по HTTP и из S3:
INSTALL httpfs;
LOAD httpfs;
CREATE SECRET (TYPE s3, KEY_ID 'ключ', SECRET 'секрет', REGION 'ru-central1', ENDPOINT 'storage.yandexcloud.net');
SELECT * FROM read_parquet('s3://bucket/data/*.parquet');
json — работа с JSON:
SELECT json_extract(payload, '$.user.name') FROM events;
spatial — геопространственные функции:
INSTALL spatial;
LOAD spatial;
SELECT ST_Distance(geom1, geom2) FROM places;
iceberg — чтение таблиц Apache Iceberg:
INSTALL iceberg;
LOAD iceberg;
SELECT * FROM iceberg_scan('s3://datalake/iceberg_table/');
MotherDuck — облачный сервис на основе DuckDB. Токен лучше передавать через переменную окружения motherduck_token, а не в строке подключения:
import duckdb
con = duckdb.connect('md:my_database') # токен из переменной окружения motherduck_token
Когда использовать DuckDB
Подходит для:
- аналитики и data science: SQL по данным, которые не помещаются в память pandas;
- ETL-пайплайнов: преобразование CSV, Parquet и JSON без загрузки в серверную БД;
- подготовки данных для дашбордов и отчётов;
- локальной аналитики вместо тяжёлых запросов к продакшн-PostgreSQL;
- аналитики в браузере через DuckDB-Wasm;
- быстрой проверки логики запросов на локальных данных.
Не подходит для:
- основного хранилища веб-приложения (берите PostgreSQL или SQLite);
- интенсивной конкурентной записи: файл может открыть на запись только один процесс;
- распределённых систем с репликацией (смотрите на ClickHouse);
- авторизации, сессий и прочих OLTP-задач.
DuckDB vs SQLite vs PostgreSQL vs ClickHouse
| Параметр | DuckDB | SQLite | PostgreSQL | ClickHouse | |---|---|---|---|---| | Класс | OLAP (встраиваемая) | OLTP (встраиваемая) | OLTP (серверная) | OLAP (серверная) | | Хранение | Колоночное | Строковое | Строковое | Колоночное | | Аналитика | ★★★★★ | ★★☆☆☆ | ★★★☆☆ | ★★★★★ | | Транзакции | ★★★☆☆ | ★★★★★ | ★★★★★ | ★☆☆☆☆ | | Встраивание | Да | Да | Нет | Нет | | Нужен сервер | Нет | Нет | Да | Да | | Масштабирование | Одна машина | Одна машина | Вертикальное + реплики | Кластер | | Python | Нативный пакет | Стандартная библиотека | psycopg | clickhouse-connect и др. | | Лицензия | MIT | Public Domain | PostgreSQL License | Apache 2.0 |
Про SQLite подробнее — в статье что такое SQLite.
DuckDB в связке с PostgreSQL
Частый паттерн: PostgreSQL для OLTP, DuckDB для аналитики. Расширение postgres позволяет читать таблицы PostgreSQL прямо из DuckDB:
import duckdb
con = duckdb.connect()
con.execute("INSTALL postgres; LOAD postgres;")
# Лучше подключаться к реплике, а не к основному серверу
con.execute("""
ATTACH 'postgresql://analyst@replica-host/myapp' AS prod (TYPE POSTGRES, READ_ONLY);
""")
result = con.execute("""
SELECT
date_trunc('week', created_at) AS week,
COUNT(*) AS new_users,
SUM(COUNT(*)) OVER (ORDER BY date_trunc('week', created_at)) AS cumulative
FROM prod.users
GROUP BY 1
ORDER BY 1
""").df()
Агрегации выполняются в DuckDB, но данные всё равно читаются из PostgreSQL, поэтому направляйте такие запросы на реплику. Пароль держите в ~/.pgpass или переменных окружения, а не в строке подключения.
Бэкап DuckDB-файлов
DuckDB хранит данные в бинарном файле .duckdb, а несброшенные изменения — в файле .duckdb.wal рядом с ним. Копировать файл, пока в него пишет приложение, небезопасно. Варианты:
# 1. Портативный экспорт в Parquet (схема + данные)
duckdb analytics.duckdb -c "EXPORT DATABASE '/tmp/duckdb_export' (FORMAT PARQUET);"
# 2. Согласованная копия в новый файл
duckdb -c "ATTACH '/var/data/analytics.duckdb' AS src (READ_ONLY);
ATTACH '/tmp/analytics-copy.duckdb' AS dst;
COPY FROM DATABASE src TO dst;"
# 3. Если приложение остановлено: CHECKPOINT и копия файла
duckdb /var/data/analytics.duckdb -c "CHECKPOINT;"
cp /var/data/analytics.duckdb /tmp/analytics-copy.duckdb
Команды 1 и 2 откроют файл, только если его не держит на запись другой процесс — DuckDB блокирует файл. Поэтому бэкап удобно делать в паузе между загрузками данных или из того же процесса, который пишет в базу. Подробно — в статье бэкап DuckDB.
Готовую копию можно загрузить в dbsend для DuckDB — каждая загрузка станет версией, с таблицами и числом строк, а на платных тарифах — с diff между версиями:
dbsend backup /tmp/analytics-copy.duckdb -d "$DBSEND_DATABASE_ID" -e duckdb -l "duckdb-$(date +%F-%H%M)"
Если рядом с файлом лежит .wal, CLI предупредит, что в копию могут попасть не все изменения. Регулярно проверяйте, что копия открывается и содержит нужные данные: см. как проверить, что бэкап рабочий.
Частые вопросы
Можно ли использовать DuckDB в продакшне?
Да. Стабильная версия 1.0 вышла в 2024 году, с тех пор формат хранения обратно совместим. DuckDB используют в продакшне для аналитических нагрузок: ETL, отчёты, встроенная аналитика. Для транзакционных задач (веб-приложения, авторизация) берите PostgreSQL или SQLite.
В чём разница между DuckDB и pandas?
Оба инструмента для анализа данных, но DuckDB работает через SQL и умеет обрабатывать данные, которые не помещаются в оперативную память, сбрасывая промежуточные результаты на диск. pandas держит весь DataFrame в памяти. Они хорошо дополняют друг друга: DuckDB читает DataFrame как таблицу и возвращает результат как DataFrame.
Чем DuckDB лучше SQLite для аналитики?
Колоночным хранением, векторным и параллельным выполнением запросов. На GROUP BY и SUM по миллионам строк DuckDB обычно значительно быстрее. SQLite с хранением по строкам лучше для точечных запросов и вставки отдельных строк. Простое правило: SQLite — для транзакций, DuckDB — для аналитики.
Работает ли DuckDB в браузере?
Да. DuckDB-Wasm компилируется в WebAssembly и выполняет запросы прямо во фронтенде, без обращения к серверу. Например, на нём построены SQL-ячейки в Observable Framework.
DuckDB бесплатный?
Да, DuckDB распространяется под лицензией MIT. MotherDuck — отдельный коммерческий облачный сервис на основе DuckDB; актуальные тарифы смотрите на его сайте. Для большинства задач хватает локального DuckDB.



