Learning Platform
Глоссарий Troubleshooting
Урок 03.04 · 20 мин
Средний
clientsjdbclanguage-bindingsecosystem

Обзор остальных клиентов: R, Node.js, Java (JDBC), Rust, Go

Python — главный клиент DuckDB, и большую часть курса мы работаем именно с ним. Но DuckDB — встраиваемый движок, и встроить его можно в программу почти на любом языке. У DuckDB есть официальные клиенты для R, Node.js, Java, Rust, Go и других сред.

Этот урок — обзорный. Цель не в том, чтобы научить вас писать на пяти языках, а в том, чтобы вы понимали: какие клиенты есть, как они устроены концептуально, и главное — что у всех них общее ядро. Поняв единый принцип, вы сможете применить DuckDB в любой из этих сред, когда понадобится. Версия — DuckDB 1.5.2.

Этот урок важен ещё и для правильного восприятия всего курса. Дальше мы будем глубоко разбирать векторизованный движок, storage-формат, сжатие, параллелизм — и все примеры будут на Python. Без понимания, что клиент это лишь обёртка, могло бы сложиться ложное впечатление, будто всё изученное относится «к Python-версии DuckDB». На самом деле это не так: изучается движок, а Python — лишь удобное окно в него.


Общий принцип: один движок, разные обёртки

Главное, что нужно усвоить про клиенты DuckDB — все они обёртки вокруг одного и того же движка.

Сам DuckDB написан на C++ и имеет стабильный C-интерфейс (C API). Клиент для любого языка — это, по сути, тонкий слой, который связывает идиоматику этого языка с C-интерфейсом движка. Когда вы исполняете запрос из R, из Java или из Go, реальную работу — парсинг, оптимизацию, векторизованное исполнение — делает один и тот же C++-движок DuckDB.

Клиенты как обёртки вокруг общего ядра
PythonКлиент для Python — обёртка над C-интерфейсом движка
RКлиент для R: тесная интеграция с data.frame и dplyr
Java (JDBC)Клиент для Java: стандартный JDBC-драйвер
Node.jsКлиент для Node.js: интеграция с экосистемой JavaScript
Rust / GoКлиенты для Rust и Go: системные языки, идиоматичные привязки
все вызывают
Движок DuckDB (C++, C API)Единое ядро: парсинг, оптимизатор, векторизованный исполнитель, storage. Реальную работу делает оно

Этот принцип — «один движок, разные обёртки» — характерен именно для встраиваемой СУБД. У клиент-серверной СУБД клиент и сервер могут различаться сильно: клиент это сетевая библиотека, сервер это отдельная программа. У DuckDB разделения нет — движок и есть библиотека, которую клиент языка просто оборачивает. Поэтому различие между клиентами здесь минимально по своей природе.

Из этого следуют три важных вывода.

Возможности SQL одинаковы во всех клиентах. Friendly SQL, типы данных, оптимизатор, расширения — это свойства движка, а не клиента. То, что работает в Python, работает и в Java, и в Go.

Производительность исполнения запроса одинакова. Запрос исполняет движок; язык-обёртка не влияет на скорость самого исполнения. Разница может быть лишь в накладных расходах вокруг — например, при материализации результата в структуры конкретного языка.

Изученное знание SQL и движка переносимо. Освоив DuckDB в Python, вы при переходе на другой клиент учите только новый синтаксис вызовов на этом языке — модель работы (соединение, запрос, результат) и весь движок остаются теми же.

NOTE

Поскольку клиенты — обёртки над одним ядром, курс намеренно учит DuckDB на Python, не распыляясь на пять языков. Всё, что вы узнаёте про SQL-диалект, типы, оптимизатор, storage-формат, параллелизм — относится к движку и одинаково верно для любого клиента. Сменить клиент — значит сменить только синтаксис обёртки.

Стоит остановиться на том, как именно устроена эта обёртка, потому что от этого зависит, какие части DuckDB видны через клиент. C-интерфейс движка (C API) — это набор функций: создать базу, создать соединение, подготовить и выполнить запрос, получить результат. Любой язык умеет вызывать функции на C через свой механизм внешних вызовов. Клиент DuckDB для конкретного языка делает две вещи. Во-первых, он вызывает функции C API под капотом. Во-вторых — и это важнее — он переводит результаты в идиоматику языка: то, что C API отдаёт как низкоуровневую структуру, клиент превращает в data.frame для R, в нативный массив для Go, в ResultSet для Java. Поэтому «толщина» клиента — это в основном слой перевода типов и результатов туда-обратно, а вся настоящая логика СУБД остаётся в общем C++-ядре под C API.

Из этого вытекает и ответ на частый вопрос: «не отстаёт ли клиент моего языка по возможностям от Python-клиента?». Возможности SQL отстать не могут — они в движке. Отставать может разве что удобство обёртки: у более популярного клиента раньше появляются вспомогательные методы, лучше документация, больше примеров. Но это вопрос зрелости конкретной обёртки, а не урезанности движка под этим клиентом.


R: исследовательская родина DuckDB

Клиент для R заслуживает особого упоминания. DuckDB задумывался в том числе как инструмент для исследователей данных, а R — один из главных языков статистики и анализа данных. R-клиент — не «ещё одна обёртка», а среда, под которую DuckDB изначально и затачивался.

Концептуально R-клиент даёт то же, что Python-клиент: соединение, исполнение SQL, получение результата. Его особенность — тесная интеграция с нативными структурами R. Результат запроса естественно ложится в data.frame R, а R-фрейм может быть запрошен SQL напрямую — как в Python с pandas DataFrame. Есть и интеграция с dplyr — популярным в R инструментом для манипуляции данными.

Для кого это важно: аналитики и исследователи, чья основная среда — R. Для них DuckDB даёт полноценный быстрый SQL-движок внутри привычной среды, без ухода в другой инструмент.

Здесь видна закономерность из урока про происхождение DuckDB. Движок создавался в исследовательской группе для людей, работающих с данными локально в R и Python. Поэтому именно у R- и Python-клиентов интеграция с нативными структурами данных языка особенно тесная: запросить локальный фрейм данных прямо в SQL, получить результат сразу как фрейм. Для других языков клиент тоже даёт полноценный доступ к движку, но настолько глубокая интеграция с «родными» табличными структурами — это в первую очередь история R и Python, и она прямо отражает аудиторию, под которую DuckDB задумывался.


Java: интеграция через JDBC

Клиент для Java устроен по стандарту экосистемы — это JDBC-драйвер.

JDBC (Java Database Connectivity) — стандартный для Java интерфейс доступа к базам данных. Любая Java-программа, умеющая работать с JDBC (а это практически любая, работающая с БД), умеет работать и с DuckDB: нужно лишь подключить JDBC-драйвер DuckDB. Знакомые JDBC-абстракции — Connection, Statement, ResultSet — работают как обычно.

Почему это удобно: JDBC — повсеместный стандарт. Многие инструменты JVM-экосистемы — приложения, BI-системы, средства разработки — умеют подключаться к источнику данных через JDBC. DuckDB как JDBC-драйвер встраивается в этот мир без специальной поддержки: для инструмента он выглядит как «ещё одна база с JDBC-драйвером».


Node.js, Rust, Go: остальной спектр

Кратко об остальных клиентах.

Node.js. Клиент для серверного JavaScript. Встраивает DuckDB в Node.js-приложения и инструменты. Полезен, когда аналитический модуль или утилита написаны на JavaScript-стеке. Отдельно стоит помнить про DuckDB-WASM — компиляцию DuckDB в WebAssembly для исполнения в браузере; это отдельная тема, и курс разбирает её в модуле 15. Связка Node.js-клиента и DuckDB-WASM показывает, насколько широко простирается «встраиваемость»: один и тот же движок работает и в серверном Node.js-процессе, и прямо во вкладке браузера.

Rust. Клиент для системного языка Rust. Идиоматичные для Rust привязки к движку. Подходит, когда DuckDB встраивается в Rust-приложение, где важны производительность и контроль над ресурсами.

Go. Клиент для Go. Встраивает DuckDB в Go-программы и сервисы — например, в аналитический бэкенд-сервис, написанный на Go.

Этот список не закрыт. Поскольку у DuckDB стабильный C API, клиент для нового языка — это, по сути, написание обёртки над уже готовым интерфейсом, и экосистема клиентов продолжает расширяться. Помимо официальных клиентов существуют и сторонние привязки для других языков. Поэтому, если ваш язык не из списка выше, имеет смысл проверить документацию и сообщество — клиент вполне может существовать. И даже если официального клиента нет, к движку всегда можно обратиться напрямую через C API из любого языка, умеющего вызывать C-функции. Стабильный C-интерфейс — это и есть то, что делает DuckDB доступным практически отовсюду.

КлиентФорма интеграцииТипичное применение
PythonБиблиотека, главный клиентПайплайны, ноутбуки, скрипты — фокус курса
RБиблиотека, интеграция с data.frame/dplyrСтатистика и исследовательский анализ
JavaJDBC-драйверJVM-приложения и инструменты с JDBC
Node.jsБиблиотека для серверного JSУтилиты и модули на JavaScript-стеке
RustИдиоматичные привязкиВстраивание в системные приложения
GoБиблиотека для GoАналитические сервисы на Go
Что одинаково и что различается между клиентами
РазличаетсяСлой обёртки: синтаксис вызовов на конкретном языке, форма интеграции (JDBC у Java), материализация в нативные структуры языка
Тонкий слой клиентаСвязывает идиоматику языка с C-интерфейсом движка — это и есть всё, что меняется между клиентами
ОдинаковоSQL-диалект и friendly SQL, типы, оптимизатор, расширения, векторизованное исполнение, storage-формат, сжатие, параллелизм
Единое ядро DuckDBC++-движок: одинаков для всех клиентов, определяет возможности SQL и производительность исполнения

Один файл базы — для всех клиентов

Есть ещё одно практическое следствие общего ядра, которое стоит выделить: формат persistent-файла у DuckDB единый. Файл .duckdb, созданный Python-клиентом, без всяких преобразований открывается Java-клиентом, R-клиентом, CLI — любым.

Это даёт удобные сценарии совместной работы. Пайплайн на Python подготовил данные и записал их в analytics.duckdb — а Java-сервис в проде читает из этого же файла через JDBC-драйвер. Или: инженер собрал базу в CLI, а коллега-исследователь открывает тот же файл R-клиентом для статистического анализа. Никакого экспорта-импорта, никакой конвертации форматов: все клиенты говорят с одним и тем же storage-форматом, потому что за ними один и тот же движок, который этот формат и определяет.

То же относится и к самому SQL. Скрипт миграции схемы или подготовки данных, написанный на SQL, можно прогнать через любой клиент — он выполнится одинаково, потому что SQL исполняет движок. Это делает SQL естественным «общим знаменателем» в команде, где разные люди работают с DuckDB из разных языков.

WARNING

Совместное использование одного файла разными клиентами не отменяет модель конкурентности DuckDB. Persistent-файл в каждый момент может быть открыт на запись только одним процессом — «один писатель». Поэтому «Python пишет, Java читает» работает как последовательность во времени или как один писатель плюс читатели, но не как два процесса, одновременно пишущих в один файл. Модель конкурентности — свойство движка, и она одна для всех клиентов.


Как выбрать клиент

Выбор клиента DuckDB на практике почти всегда тривиален: берите клиент того языка, на котором написана ваша программа. DuckDB встраивается в существующий код, а не наоборот — не выбирают язык под DuckDB.

Если аналитический пайплайн на Python — берите Python-клиент. Если исследовательский анализ ведётся в R — R-клиент. Если DuckDB встраивается в Java-сервис или подключается к JVM-инструменту — JDBC-драйвер. Если бэкенд на Go — Go-клиент.

Единственная ситуация, где выбор не так очевиден — когда задача стоит до выбора языка. Например, вы пишете утилиту с нуля, и решаете, на чём её писать. Тогда стоит учесть зрелость клиента: у более популярных клиентов (Python, R, Java) обычно богаче набор вспомогательных методов, подробнее документация, больше готовых примеров. Но это соображение второго порядка — оно про удобство разработки, а не про возможности. Любой официальный клиент даёт полный доступ к движку. В большинстве же реальных проектов язык уже выбран по причинам, не связанным с DuckDB, и тогда вопрос «какой клиент» решается сам собой.

И помните главный вывод урока: какой бы клиент вы ни выбрали, под ним один и тот же движок DuckDB. Всё содержание остальных модулей курса — SQL-диалект, типы, векторизованное исполнение, storage-формат, сжатие, параллелизм, оптимизатор — относится к движку и применимо в любом клиенте. Курс учит на Python ради конкретики примеров, но знание переносится на весь спектр клиентов.


Попробуй сам

Закрепите идею «один движок, разные обёртки» через сравнение.

  1. Откройте официальную документацию DuckDB по клиентским API (раздел Client APIs на https://duckdb.org/docs/). Посмотрите список доступных клиентов — он может оказаться шире пяти, рассмотренных в уроке.
  2. Выберите два клиента — например, Python и Java — и найдите в документации простейший пример «подключиться и выполнить запрос» для каждого. Сопоставьте: модель одинаковая (соединение, запрос, чтение результата), отличается только синтаксис языка.
  3. Сформулируйте письменно: почему такой факт, как «STANDARD_VECTOR_SIZE = 2048» или «DuckDB читает Parquet с filter pushdown», одинаково верен независимо от того, через какой клиент вы работаете? Свяжите ответ с тем, что клиент — это обёртка, а движок — общее ядро.
DataFusion Python API: тот же embedded-паттерн на Rust
Проверка знанийKnowledge check
Почему знание SQL-диалекта, типов и внутреннего устройства DuckDB переносится между всеми клиентами (Python, R, Java, Go и другими), и в чём тогда вообще разница между клиентами?
ОтветAnswer
Все клиенты DuckDB — это тонкие обёртки вокруг одного и того же движка. Сам DuckDB написан на C++ и имеет стабильный C-интерфейс; клиент для любого языка лишь связывает идиоматику этого языка с C-интерфейсом движка. Когда запрос исполняется из Python, R, Java или Go, реальную работу — парсинг, оптимизацию, векторизованное исполнение, обращение к storage — делает один и тот же C++-движок. Поэтому всё, что относится к движку — SQL-диалект и friendly SQL, система типов, оптимизатор, расширения, векторизованное исполнение, storage-формат, схемы сжатия, morsel-driven параллелизм — одинаково для любого клиента: это свойства ядра, а не обёртки. Знание движка, полученное на одном клиенте, полностью переносится на другой. Разница между клиентами лежит только в слое обёртки: синтаксис вызовов на конкретном языке, форма интеграции (например, Java-клиент это JDBC-драйвер, а R-клиент тесно интегрирован с data.frame и dplyr), и то, в какие нативные структуры языка материализуется результат. Производительность самого исполнения запроса от клиента не зависит — её определяет движок; различаться могут лишь накладные расходы вокруг, например при преобразовании результата в структуры конкретного языка. Поэтому при смене клиента инженер учит только новый синтаксис обёртки, а модель работы и весь движок остаются прежними.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Почему все клиенты DuckDB (Python, R, Java, Go и другие) дают одинаковые возможности SQL и одинаковую производительность исполнения запроса?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6