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

Клиент ClickHouse на Rust

Официальный клиент Rust для подключения к ClickHouse, изначально разработанный Paul Loyd. Исходный код клиента доступен в репозитории GitHub.

Обзор

  • Использует serde для кодирования/декодирования строк.
  • Поддерживает атрибуты serde: skip_serializing, skip_deserializing, rename.
  • Использует формат RowBinary по транспортному протоколу HTTP.
    • Планируется переход на Native через TCP.
  • Поддерживает TLS (через функции native-tls и rustls-tls).
  • Поддерживает сжатие и распаковку (LZ4).
  • Предоставляет API для выбора или вставки данных, выполнения DDL и пакетирования на стороне клиента.
  • Предоставляет удобные моки для юнит-тестирования.

Установка

Чтобы использовать crate, добавьте следующее в ваш Cargo.toml:

Смотрите также: страница на crates.io.

Особенности Cargo

  • lz4 (включено по умолчанию) — включает варианты Compression::Lz4 и Compression::Lz4Hc(_). Если включено, Compression::Lz4 используется по умолчанию для всех запросов, кроме WATCH.
  • native-tls — поддерживает URL-адреса со схемой HTTPS через hyper-tls, который ссылается на OpenSSL.
  • rustls-tls — поддерживает URL-адреса со схемой HTTPS через hyper-rustls, который не ссылается на OpenSSL.
  • inserter — включает client.inserter().
  • test-util — добавляет моки. Смотрите пример. Используйте его только в dev-dependencies.
  • watch — включает функциональность client.watch. Смотрите соответствующий раздел для подробностей.
  • uuid — добавляет serde::uuid для работы с crate uuid.
  • time — добавляет serde::time для работы с crate time.
к сведению

При подключении к ClickHouse через URL-адрес HTTPS должна быть включена либо функция native-tls, либо rustls-tls. Если обе включены, функция rustls-tls будет иметь приоритет.

Совместимость версий ClickHouse

Клиент совместим с LTS или более новыми версиями ClickHouse, а также с ClickHouse Cloud.

Сервер ClickHouse старше v22.6 неправильно обрабатывает RowBinary в некоторых редких случаях. Вы можете использовать v0.11+ и включить функцию wa-37420, чтобы решить эту проблему. Примечание: эта функция не должна использоваться с более новыми версиями ClickHouse.

Примеры

Мы стремимся охватить различные сценарии использования клиента в примерах в репозитории клиента. Обзор доступен в README примеров.

Если что-то непонятно или отсутствует в примерах или в следующей документации, не стесняйтесь связаться с нами.

Использование

примечание

crate ch2rs полезен для генерации типа строки из ClickHouse.

Создание экземпляра клиента

подсказка

Повторно используйте созданные клиенты или копируйте их, чтобы повторно использовать базовый пул соединений hyper.

Подключение по HTTPS или к ClickHouse Cloud

HTTPS работает с любыми из особенностей cargo, rustls-tls или native-tls.

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

к сведению

URL-адрес должен включать и протокол, и порт, например https://instance.clickhouse.cloud:8443.

Смотрите также:

  • Пример HTTPS с ClickHouse Cloud в репозитории клиента. Это также должно быть применимо к внутренним HTTPS соединениям.

Выбор строк

  • Плейсхолдер ?fields заменяется на no, name (поля Row).
  • Плейсхолдер ? заменяется на значения в следующих вызовах bind().
  • Удобные методы fetch_one::<Row>() и fetch_all::<Row>() могут использоваться для получения первой строки или всех строк соответственно.
  • sql::Identifier можно использовать для связывания имен таблиц.

Примечание: поскольку весь ответ передается в потоковом режиме, курсоры могут возвращать ошибки даже после того, как были получены некоторые строки. Если это произойдет в вашем случае использования, вы можете попробовать query(...).with_option("wait_end_of_query", "1"), чтобы включить буферизацию ответа на стороне сервера. Более подробная информация. Параметр buffer_size также может быть полезен.

осторожно

Используйте wait_end_of_query с осторожностью при выборе строк, так как это может привести к повышенному потреблению памяти на стороне сервера и, вероятно, снизит общую производительность.

Вставка строк

  • Если end() не вызывается, INSERT прерывается.
  • Строки отправляются постепенно в виде потока для распределения сетевой нагрузки.
  • ClickHouse вставляет партии атомарно только в том случае, если все строки помещаются в одну и ту же партицию и их количество не превышает max_insert_block_size.

Асинхронная вставка (пакетирование на стороне сервера)

Вы можете использовать асинхронные вставки ClickHouse, чтобы избежать пакетирования входящих данных на стороне клиента. Это можно сделать, просто предоставив параметр async_insert в методе insert (или даже в самом экземпляре Client, чтобы он повлиял на все вызовы insert).

Смотрите также:

Функция вставки (пакетирование на стороне клиента)

Требует активации функции cargo inserter.

  • Inserter завершает активную вставку в commit(), если один из пределов (max_bytes, max_rows, period) достигнут.
  • Интервал между завершением активных INSERT можно изменять с помощью with_period_bias, чтобы избежать пиковых нагрузок от параллельных вставщиков.
  • Inserter::time_left() можно использовать для определения, когда текущий период закончится. Вызывайте Inserter::commit() снова, чтобы проверить лимиты, если ваш поток выводит элементы редко.
  • Пороговые значения времени реализуются с помощью crate quanta для ускорения inserter. Не используется, если активирована функция test-util (поэтому время может управляться tokio::time::advance() в пользовательских тестах).
  • Все строки между вызовами commit() вставляются в одном запросе INSERT.
осторожно

Не забудьте очистить, если вы хотите завершить/завершить вставку:

Выполнение DDL

При развертывании на одном узле достаточно выполнить DDL следующим образом:

Однако, при развертывании в кластере с балансировщиком нагрузки или ClickHouse Cloud, рекомендуется дождаться, пока DDL будет применено ко всем репликам, используя опцию wait_end_of_query. Это можно сделать так:

Настройки ClickHouse

Вы можете применять различные настройки ClickHouse с помощью метода with_option. Например:

Кроме query, это также работает аналогично с методами insert и inserter; дополнительно, тот же метод можно вызывать на экземпляре Client, чтобы задать глобальные настройки для всех запросов.

Идентификатор запроса

Используя .with_option, вы можете установить опцию query_id, чтобы идентифицировать запросы в журнале запросов ClickHouse.

Кроме query, это также работает аналогично с методами insert и inserter.

осторожно

Если вы задаете query_id вручную, убедитесь, что он уникален. UUID являются хорошим выбором для этого.

Смотрите также: пример query_id в репозитории клиента.

Идентификатор сессии

Аналогично query_id, вы можете установить session_id, чтобы выполнять операторы в одной сессии. session_id может быть установлен либо глобально на уровне клиента, либо для каждого вызова query, insert или inserter.

осторожно

При развертывании в кластере, из-за отсутствия "липких сессий", вам необходимо подключиться к определенному узлу кластера, чтобы правильно использовать эту функцию, так как, например, балансировщик нагрузки "кругом" не гарантирует, что последующие запросы будут обрабатываться тем же узлом ClickHouse.

Смотрите также: пример session_id в репозитории клиента.

Пользовательские HTTP заголовки

Если вы используете прокси-аутентификацию или необходимость передать пользовательские заголовки, вы можете сделать это так:

Смотрите также: пример пользовательских HTTP заголовков в репозитории клиента.

Пользовательский HTTP клиент

Это может быть полезно для настройки параметров пула соединений HTTP.

осторожно

Этот пример основывается на устаревшем API Hyper и подлежит изменению в будущем.

Смотрите также: пример пользовательского HTTP клиента в репозитории клиента.

Типы данных

к сведению

Смотрите также дополнительные примеры:

  • (U)Int(8|16|32|64|128) сопоставляются с/из соответствующих (u|i)(8|16|32|64|128) типов или новых типов вокруг них.
  • (U)Int256 не поддерживаются напрямую, но есть обходной путь для этого.
  • Float(32|64) сопоставляются с/из соответствующих f(32|64) типов или новых типов вокруг них.
  • Decimal(32|64|128) сопоставляются с/из соответствующих i(32|64|128) типов или новых типов вокруг них. Удобнее использовать fixnum или другую реализацию фиксированных знаковых чисел с плавающей запятой.
  • Boolean сопоставляется с/из bool или новых типов вокруг него.
  • String сопоставляется с/из любых строковых или байтовых типов, например, &str, &[u8], String, Vec<u8> или SmartString. Новые типы также поддерживаются. Для хранения байтов рассмотрите возможность использования serde_bytes, так как это более эффективно.
  • FixedString(N) поддерживается как массив байтов, например, [u8; N].
  • Enum(8|16) поддерживаются с помощью serde_repr.
  • UUID сопоставляется с/из uuid::Uuid с помощью serde::uuid. Требует активации функции uuid.
  • Date сопоставляется с/из u16 или нового типа вокруг него и представляет собой количество дней, прошедших с 1970-01-01. Также поддерживается time::Date с использованием serde::time::date, что требует активации функции time.
  • Date32 сопоставляется с/из i32 или нового типа вокруг него и представляет собой количество дней, прошедших с 1970-01-01. Также поддерживается time::Date с помощью serde::time::date32, что требует активации функции time.
  • DateTime сопоставляется с/из u32 или нового типа вокруг него и представляет собой количество секунд, прошедших с эпохи UNIX. Также поддерживается time::OffsetDateTime с помощью serde::time::datetime, что требует активации функции time.
  • DateTime64(_) сопоставляется с/из i32 или нового типа вокруг него и представляет собой время, прошедшее с эпохи UNIX. Также поддерживается time::OffsetDateTime с помощью serde::time::datetime64::*, что требует активации функции time.
  • Tuple(A, B, ...) сопоставляется с/из (A, B, ...) или нового типа вокруг него.
  • Array(_) сопоставляется с/из любого среза, например, Vec<_>, &[_]. Новые типы также поддерживаются.
  • Map(K, V) ведет себя как Array((K, V)).
  • LowCardinality(_) поддерживается без проблем.
  • Nullable(_) сопоставляется с/из Option<_>. Для вспомогательных функций clickhouse::serde::* добавляйте ::option.
  • Nested поддерживается путем предоставления нескольких массивов с переименованием.
  • Типы Geo поддерживаются. Point ведет себя как кортеж (f64, f64), а остальные типы являются просто срезами точек.
  • Типы Variant, Dynamic, (новые) JSON еще не поддерживаются.

Мокирование

crate предоставляет утилиты для мокирования сервера CH и тестирования DDL, SELECT, INSERT и WATCH запросов. Эта функциональность может быть включена с помощью функции test-util. Используйте ее только как зависимость для разработки.

Смотрите пример.

Устранение неполадок

CANNOT_READ_ALL_DATA

Наиболее распространенной причиной ошибки CANNOT_READ_ALL_DATA является то, что определение строки на стороне приложения не соответствует тому, что в ClickHouse.

Рассмотрим следующую таблицу:

Затем, если EventLog определяется на стороне приложения с несовпадающими типами, например:

При вставке данных может возникнуть следующая ошибка:

В этом примере это исправляется правильным определением структуры EventLog:

Известные ограничения

  • Типы Variant, Dynamic, (новые) JSON еще не поддерживаются.
  • Связывание параметров на стороне сервера еще не поддерживается; смотрите эту проблему для отслеживания.

Связаться с нами

Если у вас есть какие-либо вопросы или нужна помощь, не стесняйтесь обратиться к нам в Community Slack или через GitHub issues.