DuckDB — embedded analytical SQL engine ("SQLite for OLAP"). In-process columnar engine with vectorized execution (1024-row batches, SIMD), reads Parquet/Arrow/CSV directly with predicate+projection pushdown. Single-writer multi-reader. Compared with Pandas, ClickHouse, Snowflake. 5 scenarios: SELECT FROM parquet (zero-copy), 1B rows aggregation on laptop, JOIN parquet × csv, DuckDB-Wasm in browser, OLTP misuse anti-pattern. 2 ADRs: DuckDB vs alternatives, vectorized execution rationale.
DuckDB — SQLite для OLAP. Embedded columnar engine, который запускается в том же процессе, что и приложение: нет сервера, нет TCP, нет кластера. Vectorized execution (1024-row batches + SIMD) даёт пропускную способность колоночной БД на ноутбуке. Читает Parquet, CSV, JSON, Arrow напрямую — без COPY INTO, без ingest, без отдельного DWH.
Главное применение: ad-hoc аналитика и ETL на single-machine workload до террабайта. Замена Pandas, когда датасет перестал помещаться в RAM, и альтернатива Spark, когда поднимать кластер ради 30-секундного запроса неоправданно.
«Pandas научился SQL, columnar storage и vectorized execution, поселился внутри твоего Python-процесса и научился читать Parquet прямо из S3 — без сервера и без ingest».
Три инварианта:
Три группы вокруг одного duckdb процесса:
jupyter + duckdb + локальные *.parquet. Edge jupyter ↔ duckdb подписан in-process: нет network hop.s3-parquet (1B rows fact table) и s3-csv (dim table). DuckDB цепляет их через extension httpfs и делает range-GET за нужными row groups.dashboard + duckdb-wasm. Тот же engine скомпилирован в WASM (~6MB) и работает прямо в табе.Все рёбра — физические каналы доступа к данным. Запросы и ответы идут по ним в обе стороны (reverse animation), отдельные «ответные» edges не нужны.
SELECT FROM parquet (zero-copy). Notebook отправляет SELECT region, SUM(bytes) FROM 's3://logs/*.parquet' WHERE day='2026-05-14' GROUP BY region. Predicate day=... и список нужных колонок (region, bytes) уезжают в Parquet reader: min/max-статистики row groups отбрасывают 90% файла, column pruning читает только два столбца из сорока. Никакого предварительного ingest — query идёт прямо по файлам в S3.
1B rows aggregation on laptop. TPC-H SF=100 (100GB, миллиард строк fact table) на M1 Mac, 16GB RAM. PRAGMA threads=8 — все ядра ноутбука. Hash table растёт; если перерастает RAM — spill на диск (медленнее, но не OOM). Итог ~90 секунд end-to-end. Spark cluster boot занял бы 5 минут только на запуск.
JOIN parquet × csv. Маленький CSV dim table читается целиком и строится hash table — broadcast hash join. Большая Parquet fact table стримится vector-by-vector через probe. Multi-format JOIN без stage в DWH, без import. CSV, JSON, Parquet, Arrow — всё «как таблицы».
DuckDB-Wasm в браузере. WASM-сборка (~6MB) грузится один раз, кэшируется браузером. Parquet fetch'ится через HTTP range request, SQL выполняется прямо в табе. Данные не покидают устройство пользователя — privacy-friendly. Так работают встроенные SQL-ноутбуки в Hex, Mode, Observable, Evidence.dev, Rill.
Anti-scenario: OLTP misuse. Web-app пытается делать 1000 concurrent INSERT через DuckDB. Single-writer file lock → 999 запросов в очереди. Нет per-row locking, нет MVCC для high concurrency. Два процесса на один .duckdb через NFS — corruption risk. Лечение: Postgres/SQLite для OLTP, DuckDB только для analytics-стороны.
ADR-001: DuckDB vs Pandas vs ClickHouse vs Snowflake.
Pandas single-threaded, ломается на 5GB. ClickHouse требует server + ZooKeeper/Keeper + replicas — overkill для одного аналитика. Snowflake — деньги плюс cold-start latency для ad-hoc запросов. Spark cluster boot ~5 минут ради 30-секундного query. Rule of thumb: если data помещается в одну машину (~1TB на современном laptop) — DuckDB; больше — distributed.
Полные ниши:
ADR-002: Vectorized execution + columnar storage.
Row-at-a-time engine (Postgres OLTP) платит per-row overhead на каждый оператор: virtual call, branch misprediction, cache miss. Запрос SUM(sales) GROUP BY region по миллиарду строк — миллиард dispatch-вызовов; 80% времени тратится на overhead, не на математику. Решение: columnar layout (contiguous memory → SIMD-friendly) плюс vectorized engine (1024-row batch per operator call). Цена — выше memory pressure и сложнее трассировка, но для OLAP trade-off очевиден.
.duckdb. File lock не для этого, плюс на NFS/SMB lock-семантика ненадёжна → corruption.LIMIT через всю таблицу из notebook. Результат материализуется в RAM ядра — OOM kernel..duckdb, потом query. Лучше query Parquet напрямую с pushdown — экономия I/O и времени.httpfs, iceberg, postgres extensions.