Перейти к содержанию
Learning Platform
Глоссарий Troubleshooting
Урок 08.05 · 35 мин
Продвинутый
Apache ArrowArrow FlightgRPCFlight SQLADBCData TransferRPCZero-Copy

Flight Protocol

Проблема: почему JDBC/ODBC не справляются

JDBC и ODBC проектировались в 1990-х для OLTP-нагрузок — одна строка за раз, request-response модель. Для аналитических запросов, возвращающих миллионы строк, это создаёт три бутылки:

  1. Сериализация: сервер конвертирует колоночные данные в строковый формат (row-by-row)
  2. Десериализация: клиент парсит строки обратно в колоночные структуры
  3. Однопоточность: один TCP-стрим, один request за раз

Результат: движок выполнил запрос за 100ms, а передача 10M строк через JDBC занимает 30 секунд.

JDBC/ODBC: три бутылки при аналитических запросах

Движок (колонки)

Движок хранит и обрабатывает данные в колоночном формате (Arrow RecordBatch). Результат запроса — набор колонок.

Row-by-row serialize

Сериализация: движок конвертирует колоночные данные в строчный формат JDBC ResultSet — одна строка за один вызов next(). Это O(rows × columns) копирований.

TCP (1 stream)

TCP-соединение: один стрим, один request/response за раз. Нет параллельных потоков данных. Нет multiplexing.

Row-by-row deserialize

Десериализация: клиент конвертирует строчные данные обратно в колоночный формат (pandas DataFrame, Arrow Table). Ещё одно O(rows × columns) копирование.

Клиент (колонки)

Клиент получает данные в нужном формате, но потратил 10-100× больше времени на передачу, чем на сам запрос.

Columnar → Row → Wire → Row → Columnar = три конвертации, одна TCP-сессия

Arrow Flight: колоночный RPC

Apache Arrow Flight — RPC-фреймворк, построенный поверх gRPC (HTTP/2 + Protocol Buffers). Вместо строчной сериализации Flight передаёт данные в формате Arrow IPC — тем же форматом, что лежит в памяти движка.

Arrow Flight: zero-serialization data transfer

Движок (Arrow)

Движок хранит результат в Arrow RecordBatch. Никакой конвертации — батч передаётся в том же формате, в каком он существует в памяти.

IPC serialize

Arrow IPC сериализация: RecordBatch → FlatBuffers metadata + raw буферы. Это O(1) — метаданные + указатели на буферы, не копирование данных (если ОС поддерживает sendfile/splice).
HTTP/2 (N streams)HTTP/2 multiplexing: несколько потоков данных по одному TCP-соединению. gRPC streams позволяют параллельную передачу нескольких RecordBatch.

IPC deserialize

IPC десериализация: FlatBuffers metadata + raw буферы → Arrow RecordBatch. Zero-copy возможен: клиент строит RecordBatch как указатели в полученный буфер.

Клиент (Arrow)

Клиент получает данные уже в Arrow-формате. Нет конвертации — данные готовы для DuckDB, Polars, pandas (через .to_pandas()).

Arrow → IPC → HTTP/2 → IPC → Arrow = zero format conversion, parallel streams

Преимущества:

  • Нет format conversion: данные остаются в Arrow IPC от сервера до клиента
  • HTTP/2 multiplexing: несколько потоков данных по одному TCP-соединению
  • gRPC streaming: server-side и bidirectional стримы — данные текут, пока генерируются
  • Параллелизм: Flight возвращает несколько endpoints — клиент забирает данные с разных нод параллельно

6 методов Flight

Flight определяет 6 RPC-методов через gRPC service definition:

Arrow Flight: 6 RPC-методов
GetFlightInfoМетаданные о наборе данных: схема, endpoints (откуда забирать), estimated rows/bytes. Клиент вызывает первым, чтобы узнать, куда обращаться за данными. Аналог EXPLAIN в SQL.
DoGetStreaming download данных. Клиент передаёт Ticket (полученный из GetFlightInfo), сервер стримит RecordBatch. Это основной метод передачи больших объёмов.
DoPutStreaming upload данных. Клиент стримит RecordBatch на сервер. Используется для bulk insert, загрузки данных в таблицу.
ListFlightsОбнаружение доступных наборов данных. Клиент передаёт Criteria (фильтр), сервер возвращает список FlightInfo. Аналог SHOW TABLES.
DoActionВыполнить произвольное действие: создать таблицу, запустить компaction, обновить кэш. Расширяемый механизм — каждый сервер определяет свои actions.
DoExchangeBidirectional streaming: клиент и сервер одновременно отправляют и получают RecordBatch. Используется для интерактивных протоколов, потоковой обработки.

Типичный flow: клиент вызывает GetFlightInfo → получает список endpoints с Ticket → вызывает DoGet на каждом endpoint параллельно → собирает RecordBatch.

Параллельный fetch: FlightEndpoint

Ключевая идея Flight — distributed data retrieval. GetFlightInfo возвращает несколько FlightEndpoint, каждый указывает на конкретный сервер:

Параллельный fetch через FlightEndpoint

Клиент → GetFlightInfo(query)

Клиент отправляет запрос координатору. GetFlightInfo возвращает FlightInfo с 3 endpoints — по одному на каждый узел кластера, хранящий часть данных.
FlightInfoFlightInfo содержит schema (общую для всех endpoints) и список FlightEndpoint — каждый с location (host:port) и ticket (идентификатор данных на этом узле).
DoGet(node-1:8815)Endpoint 1: грузит данные с node-1:8815. Ticket содержит partition ID или query fragment, специфичный для этого узла.
DoGet(node-2:8815)Endpoint 2: грузит данные с node-2:8815. Параллельно с endpoint 1 — HTTP/2 позволяет несколько соединений.
DoGet(node-3:8815)Endpoint 3: грузит данные с node-3:8815. Все три DoGet работают одновременно, клиент объединяет результаты.

Результат: concat(batches)

Клиент получает RecordBatch от каждого endpoint и конкатенирует. Порядок партиций не гарантирован — это ответственность приложения (или движка).

Это позволяет линейное масштабирование: 3 ноды → 3× пропускная способность. Для ClickHouse, Dremio, InfluxDB 3 — это штатный режим работы.

Flight SQL: SQL поверх Flight

Flight — это транспорт, не привязанный к SQL. Flight SQL — надстройка, добавляющая SQL-семантику через Protobuf-команды:

ОперацияFlight SQL командаFlight метод
Выполнить SELECTCommandStatementQueryGetFlightInfo → DoGet
Выполнить INSERT/UPDATECommandStatementUpdateDoPut
Получить список таблицCommandGetTablesGetFlightInfo → DoGet
Получить схему таблицыCommandGetTableTypesGetFlightInfo → DoGet
Prepared statementActionCreatePreparedStatementRequestDoAction
TIP

Flight SQL — это wire protocol (как MySQL protocol или PostgreSQL wire protocol). ADBC — это client API (как JDBC). Одно определяет что передаётся по сети, другое — какие функции вызывает приложение.

Databases, поддерживающие Flight SQL: Dremio, Apache Doris, InfluxDB 3, Apache Arrow DataFusion (через Ballista), StarRocks.

ADBC: Arrow Database Connectivity

ADBC — клиентский API для работы с базами данных через Arrow. Ключевое отличие от JDBC/ODBC:

  • JDBC/ODBC: row-oriented API → данные конвертируются в строки при передаче
  • ADBC: Arrow-native API → данные остаются в колоночном формате на всём пути
ADBC vs JDBC: data flow comparison

JDBC

JDBC: данные проходят через row-by-row ResultSet. Каждый next() возвращает одну строку. Для аналитики — O(rows) вызовов + конвертация в колонки на клиенте.
DB → Row ResultSet → Client
• getString(), getInt() per row
• Column → Row → Column
• Single-threaded fetch

ADBC

ADBC: данные передаются как Arrow RecordBatch. fetch_arrow_table() возвращает таблицу целиком в Arrow-формате. Нет row-by-row конвертации.
DB → Arrow RecordBatch → Client
• fetch_arrow_table() per batch
• Column → Column (no conversion)
• Parallel endpoint fetch

ADBC может работать с любым бэкендом, не только Flight SQL:

  • Flight SQL driver — для баз с Flight SQL endpoint
  • PostgreSQL driver — использует COPY ... (FORMAT binary) + Arrow конвертацию
  • SQLite driver — через нативный API + Arrow буферы
  • Snowflake driver — через Snowflake Arrow result set API
import adbc_driver_flightsql.dbapi as flight_sql

# Подключение к Flight SQL серверу
conn = flight_sql.connect("grpc+tls://my-database:8815",
 db_kwargs={"username": "user",
 "password": "pass"})

with conn.cursor() as cur:
 cur.execute("SELECT user_id, name FROM users WHERE score > 80")
 
 # Arrow-нативный результат — без row-by-row конвертации
 table = cur.fetch_arrow_table()
 print(table.schema)
 
 # Или как pandas DataFrame
 df = cur.fetch_arrow_table().to_pandas()
NOTE

ADBC реализует Python DB-API 2.0 — совместим с SQLAlchemy, Ibis и другими ORM/query builders через стандартный интерфейс connect() / cursor() / execute().

Производительность: Flight vs JDBC

Конкретные числа зависят от hardware и данных, но порядок ускорения:

СценарийJDBCFlight + ADBCУскорение
10M строк × 10 колонок~30 сек~1-2 сек15-30×
100M строк, wide table~5 мин~10-15 сек20-30×
Distributed (3 ноды)SequentialParallel endpoints3× дополнительно

Основной выигрыш — не скорость сети, а отсутствие сериализации. На localhost разница ещё заметнее: JDBC тратит CPU на row↔column конвертацию, Flight передаёт буферы напрямую.

Ключевые выводы

  1. JDBC/ODBC — row-oriented протоколы из 1990-х. Тройная конвертация (column→row→wire→row→column) — бутылка для аналитики
  2. Arrow Flight — gRPC-based RPC с данными в Arrow IPC формате. Нет format conversion, HTTP/2 multiplexing, параллельный fetch через FlightEndpoint
  3. 6 методов — GetFlightInfo (метаданные), DoGet (download), DoPut (upload), ListFlights (discovery), DoAction (extensible), DoExchange (bidirectional)
  4. FlightEndpoint — distributed data retrieval: один GetFlightInfo → N параллельных DoGet с разных нод
  5. Flight SQL — SQL-семантика (CommandStatementQuery, CommandGetTables) поверх Flight. Wire protocol, не client API
  6. ADBC — Arrow-native client API. Работает с Flight SQL, PostgreSQL, SQLite, Snowflake. DB-API 2.0 совместим
  7. Производительность — 15-30× vs JDBC на аналитических запросах. Основной выигрыш: нет row↔column конвертации
Spark Connect: Flight для client-server архитектуры DataFusion: FDAP-стек (Flight, DataFusion, Arrow, Parquet)

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

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

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

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