Перейти к основному содержимому
Перейти к основному содержимому

Интерфейс Apache Arrow Flight

Обзор

ClickHouse поддерживает протокол Apache Arrow Flight — высокопроизводительный RPC-фреймворк для эффективной передачи столбцовых данных с использованием формата Arrow IPC поверх gRPC.

Реализация также включает поддержку Arrow Flight SQL, что позволяет BI-инструментам и приложениям, работающим по протоколу Flight SQL, напрямую выполнять запросы к ClickHouse.

Ключевые возможности:

  • Выполнение SQL-запросов и получение результатов в формате Apache Arrow.
  • Вставка данных в таблицы с использованием формата Arrow.
  • Запрос метаданных (каталогов, schema, таблиц, первичных ключей) с помощью команд Flight SQL.
  • Управление сессиями и настройками с помощью действий Flight SQL.
  • Шифрование TLS и аутентификация по имени пользователя и паролю.
  • Инкрементальное получение результатов через PollFlightInfo.
  • Отмена запросов через CancelFlightInfo.

Включение сервера Arrow Flight

Чтобы включить сервер Arrow Flight, добавьте параметр arrowflight_port в конфигурацию сервера ClickHouse:

<clickhouse>
    <arrowflight_port>9090</arrowflight_port>
</clickhouse>

При запуске в журнале появляется сообщение, подтверждающее, что интерфейс активен:

{} <Information> Application: Arrow Flight compatibility protocol: 0.0.0.0:9090

Настройка TLS

Чтобы включить TLS для интерфейса Arrow Flight, настройте следующие параметры:

<clickhouse>
    <arrowflight_port>9090</arrowflight_port>
    <arrowflight>
        <enable_ssl>true</enable_ssl>
        <ssl_cert_file>/path/to/server-cert.pem</ssl_cert_file>
        <ssl_key_file>/path/to/server-key.pem</ssl_key_file>
    </arrowflight>
</clickhouse>

При включённом TLS клиенты должны подключаться по схеме grpc+tls:// вместо grpc://.

Аутентификация

В интерфейсе Arrow Flight поддерживаются два метода аутентификации:

Базовая аутентификация

Клиенты проходят аутентификацию по имени пользователя и паролю с использованием стандартного HTTP-заголовка Authorization: Basic. При успешной аутентификации сервер возвращает Bearer-токен в заголовке ответа.

Аутентификация с помощью Bearer-токена

В последующих запросах можно использовать Bearer-токен, полученный при базовой аутентификации, передавая его в заголовке Authorization: Bearer <token>. Токен автоматически обновляется при каждом использовании и истекает в соответствии с настройкой сервера default_session_timeout (по умолчанию: 60 секунд).

Пример Python

import pyarrow.flight as flight

client = flight.FlightClient("grpc://localhost:9090")

# Basic auth returns a bearer token for subsequent calls
token_pair = client.authenticate_basic_token("default", "")
options = flight.FlightCallOptions(headers=[token_pair])

При использовании TLS:

import pyarrow.flight as flight

with open("ca-cert.pem", "rb") as f:
    tls_root_certs = f.read()

client = flight.FlightClient(
    "grpc+tls://localhost:9090",
    tls_root_certs=tls_root_certs,
)

token_pair = client.authenticate_basic_token("default", "password")
options = flight.FlightCallOptions(headers=[token_pair])

Управление сеансами

Интерфейс Arrow Flight поддерживает сеансы ClickHouse через пользовательские заголовки метаданных gRPC:

ЗаголовокОписание
x-clickhouse-session-idИдентификатор сеанса. Если указан, несколько запросов используют одно и то же состояние сеанса (временные таблицы, настройки).
x-clickhouse-session-timeoutТаймаут сеанса в секундах. Не должен превышать max_session_timeout.
x-clickhouse-session-checkУстановите 1, чтобы проверить, существует ли сеанс, не создавая его.
x-clickhouse-session-closeУстановите 1, чтобы закрыть сеанс после завершения запроса. Для этого в конфигурации сервера параметр enable_arrow_close_session должен быть установлен в true.
Примечание

Поскольку Arrow Flight использует gRPC поверх HTTP/2, имена заголовков метаданных чувствительны к регистру и должны указываться в нижнем регистре точно так, как показано (например, x-clickhouse-session-id, а не X-ClickHouse-Session-Id). Это требуется RFC 9113, разделом 8.2, который предписывает, чтобы имена полей HTTP/2 содержали только символы нижнего регистра. Это отличается от HTTP/1.1, где имена заголовков регистронезависимы.

Сеансы позволяют задавать сохраняемые настройки ClickHouse через действие SetSessionOptions (см. DoAction).

Справочник по конфигурации сервера

ПараметрПо умолчаниюОписание
arrowflight_portПорт сервера Arrow Flight. Сервер запускается только в том случае, если указан этот параметр.
arrowflight.enable_sslfalseВключает шифрование TLS.
arrowflight.ssl_cert_fileПуть к файлу сертификата TLS. Обязателен, если включен TLS.
arrowflight.ssl_key_fileПуть к файлу частного ключа TLS. Обязателен, если включен TLS.
arrowflight.tickets_lifetime_seconds600Время в секундах до истечения срока действия ticket'ов и их удаления. Установите 0, чтобы отключить их автоматическое истечение.
arrowflight.cancel_ticket_after_do_getfalseЕсли true, ticket'ы отменяются сразу после обработки через DoGet, что освобождает память.
arrowflight.poll_descriptors_lifetime_seconds600Время в секундах до истечения срока действия дескрипторов опроса. Установите 0, чтобы отключить автоматическое истечение.
arrowflight.cancel_flight_descriptor_after_poll_flight_infofalseЕсли true, дескрипторы опроса отменяются после обработки через PollFlightInfo.
enable_arrow_close_sessiontrueРазрешает клиентам закрывать сеансы через заголовок x-clickhouse-session-close.
default_session_timeout60Таймаут сеанса по умолчанию в секундах. Также управляет сроком действия Bearer-токена.
max_session_timeout3600Максимально допустимый таймаут сеанса в секундах.

Поддерживаемые методы RPC

GetFlightInfo

Выполняет запрос и возвращает FlightInfo, содержащий schema результата, конечные точки с тикетами для получения данных, количество строк и байт.

Принимает FlightDescriptor, который может быть одним из следующих:

  • Дескриптор PATH: Однокомпонентный путь, интерпретируемый как имя таблицы. Генерирует SELECT * FROM <table>.
  • Дескриптор CMD: Либо строка сырого SQL-запроса, либо сериализованная protobuf-команда Flight SQL (см. Команды Flight SQL).

Запрос выполняется полностью, а результаты сохраняются в тикетах на стороне сервера. Каждый блок данных создает отдельную конечную точку/тикет, что позволяет клиентам получать данные параллельно.

# Query by table name
descriptor = flight.FlightDescriptor.for_path("my_table")
info = client.get_flight_info(descriptor, options)

# Query by SQL
descriptor = flight.FlightDescriptor.for_command(
    "SELECT * FROM my_table WHERE id > 100"
)
info = client.get_flight_info(descriptor, options)

# Retrieve results
for endpoint in info.endpoints:
    reader = client.do_get(endpoint.ticket, options)
    table = reader.read_all()
    print(table.to_pandas())

PollFlightInfo

Позволяет поэтапно получать результаты для длительно выполняющихся запросов. Вместо того чтобы ждать завершения всего запроса (как это делает GetFlightInfo), PollFlightInfo возвращает результаты по блокам.

При первом вызове запрос начинает выполняться. Ответ включает:

  • FlightInfo с конечными точками для всех блоков данных, доступных на данный момент.
  • FlightDescriptor для следующего опроса (если ожидаются дополнительные результаты).

Последующие вызовы с возвращённым дескриптором позволяют получить дополнительные блоки. Когда данных больше нет, ответ не содержит дескриптора для следующего опроса.

Примечание

Текущая реализация блокирует выполнение до тех пор, пока не станет доступен блок данных, вместо того чтобы сразу возвращать управление при отсутствии данных.

GetSchema

Возвращает schema Arrow для результата запроса без выполнения всего запроса. Принимает те же типы дескрипторов, что и GetFlightInfo.

descriptor = flight.FlightDescriptor.for_command(
    "SELECT 1 AS x, 'hello' AS y"
)
schema_result = client.get_schema(descriptor, options)
schema = schema_result.schema
print(schema)  # x: int32, y: string

DoGet

Извлекает данные по указанному тикету. Принимает одно из следующего:

  • Тикет, возвращённый GetFlightInfo или PollFlightInfo.
  • Строку сырого SQL-запроса в качестве значения тикета.
# Using a ticket from GetFlightInfo
reader = client.do_get(endpoint.ticket, options)
table = reader.read_all()

# Using a raw SQL query as ticket
ticket = flight.Ticket("SELECT number FROM system.numbers LIMIT 10")
reader = client.do_get(ticket, options)
table = reader.read_all()

DoPut

Отправляет данные в ClickHouse. Принимает FlightDescriptor и поток пакетов записей Arrow.

Вставка по имени таблицы (дескриптор PATH):

schema = pa.schema([("id", pa.int64()), ("name", pa.string())])
batch = pa.record_batch(
    [pa.array([1, 2, 3]), pa.array(["Alice", "Bob", "Charlie"])],
    schema=schema,
)

descriptor = flight.FlightDescriptor.for_path("my_table")
writer, _ = client.do_put(descriptor, schema, options)
writer.write_batch(batch)
writer.close()

Вставка через SQL (дескриптор CMD):

descriptor = flight.FlightDescriptor.for_command(
    "INSERT INTO my_table FORMAT Arrow"
)
writer, _ = client.do_put(descriptor, schema, options)
writer.write_batch(batch)
writer.close()

Выполнение DDL/DML через Flight SQL CommandStatementUpdate:

Клиенты Flight SQL используют CommandStatementUpdate для выполнения операторов DDL/DML (CREATE, INSERT, ALTER и т. д.). В ответе возвращается количество затронутых строк.

Массовый приём данных через Flight SQL CommandStatementIngest:

Поддерживается только добавление данных в существующие таблицы (TABLE_NOT_EXIST_OPTION_FAIL + TABLE_EXISTS_OPTION_APPEND). Каталоги и временные таблицы для этой команды не поддерживаются.

Примечание

Для передачи данных поддерживается только формат Arrow. Указание других форматов в SQL (например, FORMAT JSON) приводит к ошибке.

DoAction

Выполняет именованные действия. Поддерживаются следующие действия:

CancelFlightInfo

Отменяет выполняемый запрос, связанный с FlightInfo. Идентификатор запроса извлекается из поля app_metadata объекта FlightInfo. Также отменяет все дескрипторы опроса, связанные с этим запросом.

# Start a long-running query via PollFlightInfo, then cancel it
cancel_request = flight.CancelFlightInfoRequest(info)
result = client.cancel_flight_info(cancel_request, options)
# result.status is CancelStatus.CANCELLED if successful

SetSessionOptions

Устанавливает настройки сервера ClickHouse для текущего сеанса. Для этого требуется идентификатор сеанса, заданный в заголовке x-clickhouse-session-id.

Поддерживаемые типы значений: string, boolean, integer, double и списки строк.

Если имя настройки неизвестно, возвращается ошибка INVALID_NAME. Если значение не удаётся обработать, возвращается ошибка INVALID_VALUE.

GetSessionOptions

Возвращает все текущие настройки ClickHouse и их значения для текущего сеанса. Возвращает отображение, где ключи — имена настроек, а значения — строки (внутренне выполняет запрос к system.settings).

Команды Flight SQL

Когда дескриптор CMD содержит сериализованное protobuf-сообщение Flight SQL, ClickHouse обрабатывает следующие команды:

Поддерживается через GetFlightInfo / GetSchema

CommandDescription
CommandStatementQueryВыполнить произвольный SQL-запрос.
CommandGetSqlInfoПолучить метаданные сервера (имя, версия, версия Arrow, поддерживаемые возможности).
CommandGetCatalogsВывести список каталогов. Возвращает пустой результат (ClickHouse не использует каталоги).
CommandGetDbSchemasВывести список баз данных. Поддерживает необязательный параметр db_schema_filter_pattern (шаблон SQL LIKE).
CommandGetTablesВывести список таблиц. Поддерживает фильтры по schema, имени таблицы, типам таблиц и необязательное включение schema.
CommandGetTableTypesВывести список типов движков таблиц (из system.table_engines).
CommandGetPrimaryKeysПолучить столбцы первичного ключа для указанной таблицы.

Поддерживается через DoPut

КомандаОписание
CommandStatementUpdateВыполняет DDL/DML-оператор (CREATE, INSERT, ALTER и т. д.). Возвращает количество затронутых строк.
CommandStatementIngestВыполняет массовую вставку данных Arrow в существующую таблицу. Поддерживается только режим добавления.

Пока не реализовано

КомандаСтатус
CommandGetCrossReferenceНе реализовано
CommandGetExportedKeysНе реализовано
CommandGetImportedKeysНе реализовано
CommandStatementSubstraitPlanНе поддерживается (поддержка Substrait отсутствует)
CommandPreparedStatementQueryНе реализовано
CommandPreparedStatementUpdateНе реализовано

Полный пример

import pyarrow as pa
import pyarrow.flight as flight

# Connect and authenticate
client = flight.FlightClient("grpc://localhost:9090")
token = client.authenticate_basic_token("default", "")
options = flight.FlightCallOptions(headers=[token])

# Insert data using DoPut with a PATH descriptor
schema = pa.schema([("id", pa.uint32()), ("value", pa.string())])
batch = pa.record_batch(
    [pa.array([1, 2, 3], type=pa.uint32()), pa.array(["a", "b", "c"])],
    schema=schema,
)
descriptor = flight.FlightDescriptor.for_path("test")
writer, _ = client.do_put(descriptor, schema, options)
writer.write_batch(batch)
writer.close()

# Query data using GetFlightInfo + DoGet
descriptor = flight.FlightDescriptor.for_command(
    "SELECT * FROM test ORDER BY id"
)
info = client.get_flight_info(descriptor, options)
for endpoint in info.endpoints:
    reader = client.do_get(endpoint.ticket, options)
    table = reader.read_all()
    print(table.to_pandas())

Вывод:

   id value
0   1     a
1   2     b
2   3     c

Формат данных

Все данные передаются в формате Apache Arrow IPC. Поддерживается только формат Arrow — при указании других форматов ClickHouse (например, FORMAT JSON, FORMAT CSV) возникает ошибка.

Во время сериализации типы данных ClickHouse сопоставляются с типами Arrow. Параметр output_format_arrow_unsupported_types_as_binary определяет, будут ли неподдерживаемые типы ClickHouse сериализоваться как бинарные объекты BLOB.

Совместимость

Интерфейс Arrow Flight совместим с любым клиентом или инструментом, поддерживающим протокол Arrow Flight или Arrow Flight SQL, включая:

  • Python (pyarrow)
  • Java (org.apache.arrow.flight)
  • C++ (arrow::flight)
  • Go (apache/arrow/go)
  • драйверы ADBC (Arrow Database Connectivity)
  • DBeaver и другие инструменты с поддержкой Flight SQL

Если для вашего инструмента доступен нативный коннектор ClickHouse (например, JDBC, ODBC или нативный протокол), предпочтительно использовать его, если только Arrow Flight не требуется именно из-за производительности или совместимости форматов.

Возможности ArrowFlight на стороне клиента

ClickHouse также может выступать в роли клиента Arrow Flight для чтения данных с внешних серверов Arrow Flight. См.:

См. также