Клиент 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 и пакетной отправки на стороне клиента.
- Предоставляет удобные заглушки (mocks) для модульного тестирования.
Установка
Чтобы использовать этот крейт, добавьте следующее в свой Cargo.toml:
См. также страницу пакета на crates.io: clickhouse.
Возможности 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 по URL со схемой HTTPS должна быть включена одна из возможностей: native-tls или rustls-tls.
Если включены обе, приоритет будет у rustls-tls.
Совместимость с версиями ClickHouse
Клиент совместим с LTS-версиями и более новыми версиями ClickHouse, а также с ClickHouse Cloud.
Сервер ClickHouse версии ниже v22.6 обрабатывает RowBinary некорректно в некоторых редких случаях.
Вы можете использовать v0.11+ и включить флаг функции wa-37420, чтобы решить эту проблему. Примечание: этот флаг не следует использовать с более новыми версиями ClickHouse.
Примеры
Мы стремимся охватить различные сценарии использования клиента с помощью примеров в репозитории клиента. Обзор приведён в файле README для examples.
Если что‑то остаётся непонятным или чего‑то не хватает в примерах или в приведённой ниже документации, вы можете связаться с нами.
Использование
Crate ch2rs полезен для генерации типа строки из ClickHouse.
Создание экземпляра клиента
Повторно используйте созданные клиенты или клонируйте их, чтобы повторно использовать базовый пул соединений hyper.
Подключение по HTTPS или к ClickHouse Cloud
HTTPS поддерживается при использовании 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 в репозитории клиента.
ID сессии
Аналогично query_id, вы можете задать session_id, чтобы выполнять команды в одной и той же сессии. session_id можно задать либо глобально на уровне клиента, либо для каждого вызова query, insert или inserter.
В кластерных развертываниях из‑за отсутствия «липких сессий» необходимо оставаться подключённым к конкретному узлу кластера, чтобы корректно использовать эту возможность, поскольку, например, балансировщик нагрузки с алгоритмом round-robin не гарантирует, что последующие запросы будут обрабатываться тем же самым узлом ClickHouse.
См. также: пример session_id в репозитории клиента.
Пользовательские HTTP-заголовки
Если вы используете аутентификацию на прокси-сервере или вам нужно передать дополнительные заголовки, вы можете сделать это так:
См. также: пример пользовательских HTTP-заголовков в репозитории клиента.
Пользовательский HTTP‑клиент
Это может быть полезно для тонкой настройки параметров нижележащего пула HTTP‑соединений.
Этот пример основан на устаревшем Hyper API и в будущем может быть изменён.
См. также: пример с пользовательским 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. Требуется фичаuuid.
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(featuretime).
DateTime64(_)маппится в/изi32или newtype-обёртку вокруг него и представляет время, прошедшее с момента начала эпохи Unix. Также поддерживаетсяtime::OffsetDateTimeпри использованииserde::time::datetime64::*, для чего требуется включить featuretime.
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пока не поддерживаются.
Мокирование
Crate предоставляет утилиты для мокирования сервера ClickHouse и тестирования DDL-, SELECT-, INSERT- и WATCH-запросов. Эту функциональность можно включить с помощью опции test-util. Используйте её только как зависимость для разработки.
См. пример.
Устранение неполадок
CANNOT_READ_ALL_DATA
Наиболее распространённая причина ошибки CANNOT_READ_ALL_DATA заключается в том, что определение строки на стороне приложения не совпадает с определением строки в ClickHouse.
Рассмотрим следующую таблицу:
Затем, если EventLog определён на стороне приложения с несовместимыми типами, например:
При вставке данных может произойти следующая ошибка:
В этом примере это исправляется за счет корректного определения структуры EventLog:
Известные ограничения
- Типы данных
Variant,Dynamicи (новый)JSONпока не поддерживаются. - Привязка параметров на стороне сервера пока не поддерживается; см. эту задачу для отслеживания прогресса.
Свяжитесь с нами
Если у вас есть вопросы или нужна помощь, свяжитесь с нами в нашем Community Slack или через раздел issues на GitHub.