Клиент 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.
IPv6сопоставляется с/изstd::net::Ipv6Addr.IPv4сопоставляется с/изstd::net::Ipv4Addrс помощьюserde::ipv4.
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.