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, и клиентской пакетной обработки.
- Предоставляет удобные моки для юнит-тестирования.
Установка
Чтобы использовать пакет, добавьте следующее в ваш 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для работы с uuid пакетом.time— добавляетserde::timeдля работы с time пакетом.
При подключении к ClickHouse по HTTPS URL либо функция native-tls, либо rustls-tls должна быть активирована.
Если обе функции включены, приоритет будет у функции rustls-tls.
Совместимость версий ClickHouse
Клиент совместим с LTS версиями или новее ClickHouse, а также с ClickHouse Cloud.
Сервер ClickHouse версии ниже v22.6 обрабатывает RowBinary некорректно в некоторых редких случаях.
Вы можете использовать v0.11+ и включить функцию wa-37420, чтобы решить эту проблему. Примечание: эту функцию не следует использовать с новыми версиями ClickHouse.
Примеры
Мы стремимся охватить различные сценарии использования клиента в примерях в репозитории клиента. Обзор доступен в README примеров.
Если что-то неясно или отсутствует в примерах или в следующей документации, не стесняйтесь связаться с нами.
Использование
Пакет ch2rs полезен для генерации типа строки из ClickHouse.
Создание экземпляра клиента
Повторно используйте созданные клиенты или клонируйте их, чтобы повторно использовать пул соединений hyper.
Подключение через HTTPS или ClickHouse Cloud
HTTPS работает либо с функцией 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).
Смотрите также:
- Пример асинхронной вставки в репозитории клиента.
Функция inserter (пакетная обработка на стороне клиента)
Требуется функция inserter в Cargo.
Inserterзавершает активную вставку вcommit(), если некоторые из пределов (max_bytes,max_rows,period) достигаются.- Интервал между завершениями активных
INSERTможет быть скорректирован с помощьюwith_period_bias, чтобы избежать всплесков нагрузки параллельными вставщиками. Inserter::time_left()может использоваться для определения, когда заканчивается текущий период. Повторно вызывайтеInserter::commit(), чтобы проверить лимиты, если ваша последовательность редко генерирует элементы.- Пороговые значения времени реализованы с помощью пакета 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, чтобы задать общие настройки для всех запросов.
ID запроса
Используя .with_option, вы можете задать опцию query_id, чтобы идентифицировать запросы в журнале запросов ClickHouse.
Кроме query, он работает аналогично с методами insert и inserter.
Если вы задаете query_id вручную, убедитесь, что он уникален. UUID являются хорошим выбором для этого.
Смотрите также: пример query_id в репозитории клиента.
ID сессии
Аналогично query_id, вы можете установить session_id, чтобы выполнять операторы в одной сессии. session_id может быть установлен либо глобально на уровне клиента, либо для вызова query, insert или inserter.
При кластерных развертываниях, из-за отсутствия "липких сессий", вам нужно быть подключенным к определенному узлу кластера, чтобы правильно использовать эту функцию, так как, например, балансировщик нагрузки типа round-robin не гарантирует, что последующие запросы будут обработаны тем же узлом 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еще не поддерживаются.
Мокирование
Пакет предоставляет утилиты для мокирования сервера 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.