Rust-клиент ClickHouse
Официальный 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 для примеров.
Если в примерах или в приведённой ниже документации что-то непонятно или чего-то не хватает, вы можете связаться с нами.
Использование
Crate ch2rs полезен для генерации типа строки на основе схемы ClickHouse.
Создание экземпляра клиента
Повторно используйте уже созданные экземпляры клиента или клонируйте их, чтобы использовать общий пул соединений hyper.
Подключение по HTTPS или к ClickHouse Cloud
HTTPS работает как с функциями (features) Cargo rustls-tls, так и с native-tls.
Затем создайте клиент обычным образом. В этом примере переменные окружения используются для хранения параметров подключения:
URL должен включать и протокол, и порт, например https://instance.clickhouse.cloud:8443.
См. также:
- Пример HTTPS с ClickHouse Cloud в репозитории клиента. Его также можно использовать для HTTPS-подключений к on-premise‑инстансам.
Выбор строк
- Заполнитель
?fieldsзаменяется наno, name(поляRow). - Заполнитель
?заменяется значениями в последующих вызовахbind(). - Удобные методы
fetch_one::<Row>()иfetch_all::<Row>()можно использовать для получения, соответственно, первой строки или всех строк. sql::Identifierможно использовать для привязки имён таблиц.
NB: так как весь ответ передаётся в потоке, курсоры могут вернуть ошибку даже после того, как уже были отданы некоторые строки. Если в вашем случае это происходит, вы можете попробовать 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 (клиентская пакетная запись)
Требуется фича Cargo inserter.
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.
Не забудьте выполнить flush, если вы хотите завершить/финализировать вставку:
Выполнение операторов 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.
В случае кластерных развертываний из‑за отсутствия «sticky sessions» необходимо подключаться к конкретному узлу кластера, чтобы корректно использовать эту возможность, поскольку, например, балансировщик нагрузки с алгоритмом 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)или newtype-обёртками вокруг них. -
(U)Int256не поддерживаются напрямую, но существует обходной путь. -
Float(32|64)сопоставляется с соответствующимиf(32|64)или newtype-обёртками вокруг них. -
Decimal(32|64|128)сопоставляется с соответствующимиi(32|64|128)или newtype-обёртками вокруг них. Удобнее использоватьfixnumили другую реализацию знаковых чисел с фиксированной запятой. -
Booleanсопоставляется сboolили newtype-обёртками вокруг него. -
Stringсопоставляется с любыми строковыми или байтовыми типами, например&str,&[u8],String,Vec<u8>илиSmartString. Также поддерживаются новые типы. Для хранения байтов рекомендуется использоватьserde_bytes, поскольку это эффективнее.
FixedString(N)поддерживает представление в виде массива байтов, например[u8; N].
- Перечисления
Enum(8|16)поддерживаются с использованиемserde_repr.
UUIDсопоставляется сuuid::Uuidи обратно с помощьюserde::uuid. Требует включения featureuuid.
IPv6сопоставляется сstd::net::Ipv6Addrи обратно.IPv4сопоставляется сstd::net::Ipv4Addrи обратно с помощьюserde::ipv4.
Dateсопоставляется сu16(или newtype-обёрткой вокруг него) и представляет количество дней, прошедших с1970-01-01. Также поддерживаетсяtime::Dateпри использованииserde::time::date, что требует включённой фичиtime.
Date32маппится из/вi32или newtype-обёртку вокруг него и представляет количество дней, прошедших с1970-01-01. Также поддерживаетсяtime::Dateпри использованииserde::time::date32, для чего требуется включённая фичаtime.
DateTimeсопоставляется сu32или newtype-обёрткой вокруг него и представляет количество секунд, прошедших с эпохи UNIX. Также поддерживаетсяtime::OffsetDateTimeпри использованииserde::time::datetime, для чего требуется фичаtime.
DateTime64(_)сопоставляется сi32или newtype-обёрткой вокруг него и представляет количество времени, прошедшее с эпохи UNIX. Также поддерживаетсяtime::OffsetDateTimeпри использованииserde::time::datetime64::*, для чего требуется фичаtime.
Tuple(A, B, ...)сопоставляется с(A, B, ...)или обёрткой newtype вокруг него.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. Функциональность может быть включена с помощью feature test-util. Используйте её только как dev-зависимость.
См. пример.
Устранение неполадок
CANNOT_READ_ALL_DATA
Наиболее распространённой причиной ошибки CANNOT_READ_ALL_DATA является то, что описание строки на стороне приложения не соответствует описанию строки в ClickHouse.
Рассмотрим следующую таблицу:
Затем, если EventLog определён в приложении с несовместимыми типами, например:
При вставке данных может возникнуть следующая ошибка:
В этом примере это устраняется правильным определением структуры EventLog:
Известные ограничения
- Типы данных
Variant,Dynamicи (новый)JSONпока не поддерживаются. - Привязка параметров на стороне сервера пока не поддерживается; для отслеживания см. эту задачу.
Свяжитесь с нами
Если у вас есть вопросы или нужна помощь, вы можете написать нам в Community Slack или создать обращение в разделе Issues на GitHub.