EXPORT DATABASE и логическая переносимость
В прошлом уроке мы выяснили: backward-совместимость storage-формата гарантирована, forward — нет, и для совместимости со старым движком есть STORAGE_VERSION. Но всё это — про совместимость одного бинарного формата. Иногда нужна переносимость принципиально иного рода: вынуть данные из мира DuckDB вообще — в формат, который прочитает что угодно. Для этого есть EXPORT DATABASE и IMPORT DATABASE. Этот завершающий урок модуля про то, чем логический дамп отличается от бинарного файла, почему он переносим куда шире и где он незаменим.
Два способа сохранить базу
Сохранить состояние базы DuckDB можно двумя принципиально разными способами.
Физический способ — это сам файл .duckdb. Это бинарный снимок: блоки, row groups, сжатые сегменты, метаданные — всё ровно в том виде, в каком движок держит данные на диске. Скопировали файл — получили точную копию базы, байт в байт. Открывается такая копия мгновенно: движку не нужно ничего пересобирать, он просто читает готовую структуру.
Логический способ — это EXPORT DATABASE. Он не копирует блоки. Он выгружает содержимое базы как набор обычных файлов: данные каждой таблицы — в Parquet или CSV, а схему — определения таблиц, представлений, последовательностей, индексов — в текстовый SQL-скрипт. На выходе не бинарный снимок, а директория с человеко- и машиночитаемыми артефактами.
Как работает EXPORT DATABASE
Синтаксис прост. Команда принимает директорию назначения и опционально формат данных:
-- Выгрузить всю текущую базу в директорию, данные в формате Parquet
EXPORT DATABASE 'dump_dir' (FORMAT parquet);
После выполнения в dump_dir появляется характерный набор файлов:
ls dump_dir
schema.sql
load.sql
orders.parquet
customers.parquet
products.parquet
Разберём, что есть что. schema.sql — DDL-скрипт: CREATE TABLE, CREATE VIEW, CREATE SEQUENCE и прочие определения структуры, без данных. load.sql — скрипт загрузки: серия команд COPY, которые заливают данные из Parquet-файлов обратно в созданные таблицы. И по одному файлу данных на каждую таблицу — orders.parquet, customers.parquet и так далее, каждый содержит все строки своей таблицы.
Формат данных выбираете вы. FORMAT parquet — компактный колоночный бинарный формат, со сжатием, обычно лучший выбор. FORMAT csv — текстовый, читаемый глазами, годится для мелких таблиц и для случаев, когда дамп нужно посмотреть или отдать инструменту, который Parquet не понимает.
Обратная операция — IMPORT DATABASE — забирает директорию дампа и пересобирает из неё базу:
-- В новой пустой базе восстановить всё из директории дампа
IMPORT DATABASE 'dump_dir';
IMPORT DATABASE под капотом просто выполняет schema.sql (создаёт таблицы и прочие объекты), а затем load.sql (заливает данные через COPY). Никакой магии: это исполнение двух обычных SQL-скриптов. Поэтому дамп можно применить и вручную, по частям, — например, выполнить только schema.sql, чтобы получить пустую базу с нужной структурой.
Почему логический дамп переносим шире бинарного файла
Главное достоинство EXPORT DATABASE — переносимость, и она шире бинарной по нескольким независимым осям.
Поверх версий DuckDB. Бинарный файл привязан к storage version, и forward-совместимость не гарантирована: файл формата 68 может не открыться на DuckDB 1.2. Дамп от этой проблемы свободен. schema.sql — это обычный SQL-текст, load.sql — обычные COPY, Parquet — стабильный, давно зафиксированный формат. Импортировать такой дамп можно практически любой версией DuckDB, и старой, и будущей. Логический дамп — это, по сути, способ «переехать» между версиями формата, когда прямая бинарная совместимость не работает.
Поверх движков вообще. Это и есть качественный скачок. orders.parquet — не специфичный для DuckDB файл. Его прочитает Spark, Pandas, Polars, Trino, Arrow, BigQuery, ClickHouse — Parquet понимает практически вся аналитическая экосистема. schema.sql — это текст, его можно прочитать глазами и при необходимости адаптировать под диалект другой СУБД. То есть EXPORT DATABASE — это способ вынуть данные из DuckDB наружу, в инструментально-нейтральном виде. Бинарный .duckdb так не умеет: его, кроме DuckDB, не прочитает никто.
Прозрачность и инспектируемость. Бинарный файл — чёрный ящик: чтобы понять, что внутри, нужен сам DuckDB. Дамп открыт: schema.sql можно прочитать в любом редакторе, Parquet-файлы — просмотреть любым Parquet-инструментом, посчитать в них строки, проверить типы. Это удобно для аудита, для отладки, для контроля версий — schema.sql отлично кладётся в Git, и diff покажет, как менялась структура базы.
| Свойство | Бинарный .duckdb | Логический дамп (EXPORT) |
|---|---|---|
| Скорость открытия | Мгновенно, без пересборки | Нужен IMPORT: создание таблиц + COPY |
| Совместимость между версиями DuckDB | Backward да, forward best-effort | Очень широкая, и назад, и вперёд |
| Читается другими движками | Нет, только DuckDB | Да — Parquet/CSV универсальны |
| Инспектируемость | Чёрный ящик | Открытые читаемые файлы |
| Точность копии | Байт в байт, включая физраскладку | Логически идентична, физраскладка пересоздаётся |
Где EXPORT DATABASE незаменим
Из этих свойств вытекают конкретные сценарии, в которых логический дамп — правильный, а часто и единственный инструмент.
Миграция между несовместимыми версиями формата. Нужно перенести данные со старого DuckDB на новый или наоборот, а прямая бинарная совместимость в нужную сторону не работает. EXPORT старой версией, IMPORT новой — и данные переехали, минуя проблему storage version.
Усадка файла. В уроке про блоки мы видели: DROP TABLE отправляет блоки в free list, но файл на диске не уменьшается. EXPORT DATABASE с последующим IMPORT DATABASE в свежий файл пересобирает базу с нуля — без накопленных свободных блоков, плотно. Это штатный способ реально вернуть место операционной системе.
Бэкап в открытом формате. Бинарный бэкап рискует не открыться на будущей версии (forward-совместимость не гарантирована). Дамп в Parquet — это бэкап, который точно прочитается и через годы, и не только DuckDB. Для долговременного архива логический дамп надёжнее.
Передача данных в другую систему. Нужно отдать содержимое базы в Spark-пайплайн, в облачное хранилище, в другую СУБД. EXPORT DATABASE сразу даёт Parquet-файлы — готовый, всем понятный формат обмена. Не нужно ничего конвертировать отдельно.
Простое правило выбора. Нужна быстрая точная копия для той же или совместимой версии DuckDB — копируйте файл .duckdb, это мгновенно. Нужно пережить смену версии формата, уменьшить разросшийся файл, сделать долговременный бэкап или отдать данные за пределы DuckDB — используйте EXPORT DATABASE. Физический файл оптимизирован под скорость и точность, логический дамп — под переносимость и долговечность.
Попробуй сам
Пройдите полный цикл экспорта и импорта.
- Создайте базу с несколькими таблицами:
duckdb shop.duckdb, затемCREATE TABLE orders AS SELECT range AS id, range % 50 AS customer FROM range(200000);иCREATE TABLE customers AS SELECT range AS id, ('name_' || range) AS name FROM range(50);. - Выгрузите базу в Parquet:
EXPORT DATABASE 'shop_dump' (FORMAT parquet);. Выйдите и посмотрите содержимое директории:ls -la shop_dump. Найдитеschema.sql,load.sqlи по одному.parquetна таблицу. - Откройте
schema.sqlв редакторе — это обычный SQL сCREATE TABLE. Откройтеload.sql— это командыCOPY. Убедитесь, что дамп прозрачен. - Создайте пустую новую базу и восстановите в неё дамп:
duckdb shop_restored.duckdb, затемIMPORT DATABASE 'shop_dump';. ПроверьтеSELECT count(*) FROM orders;— данные на месте. - Докажите межинструментальную переносимость: прочитайте Parquet-файл из дампа другим инструментом или прямо в новой in-memory сессии DuckDB как внешний файл —
SELECT count(*) FROM 'shop_dump/orders.parquet';. Файл читается напрямую, без всякогоIMPORT. - Сравните на диске размер исходного
shop.duckdbи суммарный размер директорииshop_dump. Прокомментируйте разницу: оба сжаты, но это разные форматы с разной раскладкой.
Этот эксперимент показывает на практике, что EXPORT DATABASE превращает закрытый бинарный файл в открытый набор универсальных артефактов — и почему это качественно более переносимо.