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

Java-клиент

Клиентская библиотека Java для взаимодействия с сервером БД по его протоколам. Текущая реализация поддерживает только HTTP-интерфейс. Библиотека предоставляет собственный API для отправки запросов на сервер, а также инструменты для работы с различными форматами бинарных данных (RowBinary* & Native*).

Настройка


<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>client-v2</artifactId>
    <version>0.9.4</version>
</dependency>

Инициализация

Объект Client инициализируется с помощью com.clickhouse.client.api.Client.Builder#build(). Каждый клиент имеет собственный контекст, объекты между клиентами не являются общими. Builder предоставляет методы конфигурации для удобной настройки.

Пример:

 Client client = new Client.Builder()
                .addEndpoint("https://clickhouse-cloud-instance:8443/")
                .setUsername(user)
                .setPassword(password)
                .build();

Client реализует интерфейс AutoCloseable и должен быть закрыт, когда он больше не нужен.

Аутентификация

Аутентификация настраивается для каждого клиента на этапе инициализации. Поддерживаются три метода аутентификации: по паролю, по токену доступа и по клиентскому SSL-сертификату.

Для аутентификации по паролю необходимо задать имя пользователя и пароль, вызвав методы setUsername(String) и setPassword(String):

 Client client = new Client.Builder()
        .addEndpoint("https://clickhouse-cloud-instance:8443/")
        .setUsername(user)
        .setPassword(password)
        .build();

Для аутентификации с помощью токена доступа необходимо установить токен доступа, вызвав setAccessToken(String):

 Client client = new Client.Builder()
        .addEndpoint("https://clickhouse-cloud-instance:8443/")
        .setAccessToken(userAccessToken)
        .build();

Аутентификация по SSL-сертификату клиента требует указания имени пользователя, включения SSL-аутентификации, указания клиентского сертификата и клиентского ключа путем вызова методов setUsername(String), useSSLAuthentication(boolean), setClientCertificate(String) и setClientKey(String) соответственно:

Client client = new Client.Builder()
        .useSSLAuthentication(true)
        .setUsername("some_user")
        .setClientCertificate("some_user.crt")
        .setClientKey("some_user.key")
Примечание

Аутентификацию SSL может быть сложно диагностировать в продакшене, поскольку многие ошибки из библиотек SSL не предоставляют достаточно информации. Например, если клиентский сертификат и ключ не совпадают, сервер немедленно разорвет соединение (в случае HTTP это произойдет на этапе инициализации соединения, когда HTTP-запросы еще не отправлены и ответ не будет отправлен).

Для проверки сертификатов и ключей используйте такие инструменты, как openssl:

  • проверьте целостность ключа: openssl rsa -in [key-file.key] -check -noout
  • убедитесь, что в клиентском сертификате значение CN совпадает с именем пользователя:
    • получить CN из пользовательского сертификата — openssl x509 -noout -subject -in [user.cert]
    • проверить, что то же значение указано в базе данных select name, auth_type, auth_params from system.users where auth_type = 'ssl_certificate' (запрос вернёт auth_params со значением вида {"common_names":["some_user"]})

Конфигурация

Все настройки определяются методами экземпляра (также известными как методы конфигурации), которые четко определяют область видимости и контекст каждого значения. Основные параметры конфигурации определяются в одной области видимости (клиент или операция) и не переопределяют друг друга.

Конфигурация задаётся при создании клиента. См. com.clickhouse.client.api.Client.Builder.

Настройка клиента

MethodArgumentsDescriptionDefaultKey
addEndpoint(String endpoint)endpoint - адрес сервера в формате URLДобавляет endpoint сервера в список доступных серверов. В настоящее время поддерживается только один endpoint.nonenone
addEndpoint(Protocol protocol, String host, int port, boolean secure)protocol - протокол подключения
host - IP-адрес или имя хоста
secure - использовать HTTPS
Добавляет endpoint сервера в список доступных серверов. В настоящее время поддерживается только один endpoint.nonenone
enableConnectionPool(boolean enable)enable - флаг включения/отключенияУстанавливает, включен ли пул соединенийtrueconnection_pool_enabled
setMaxConnections(int maxConnections)maxConnections - число соединенийУстанавливает, сколько соединений клиент может открыть к каждому endpoint-у сервера.10max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit)timeout - значение тайм-аута
unit - единица времени
Устанавливает TTL соединения, по истечении которого соединение будет считаться неактивным-1connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit)timeout - значение тайм-аута
unit - единица времени
Устанавливает тайм-аут Keep-Alive для HTTP-соединения. Установите 0, чтобы отключить Keep-Alive.-http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy)strategy - LIFO или FIFOВыбирает стратегию повторного использования соединений, которую должен применять пул соединенийFIFOconnection_reuse_strategy
setDefaultDatabase(String database)database - имя базы данныхУстанавливает базу данных по умолчанию.defaultdatabase

Настройки сервера

Настройки на стороне сервера можно задать на уровне клиента однократно при создании (см. метод serverSetting класса Builder) и на уровне операции (см. serverSetting для класса настроек операции).

 try (Client client = new Client.Builder().addEndpoint(Protocol.HTTP, "localhost", mockServer.port(), false)
        .setUsername("default")
        .setPassword(ClickHouseServerForTest.getPassword())
        .compressClientRequest(true)

        // Client level
        .serverSetting("max_threads", "10")
        .serverSetting("async_insert", "1")
        .serverSetting("roles", Arrays.asList("role1", "role2"))

        .build()) {

	// Operation level
	QuerySettings querySettings = new QuerySettings();
	querySettings.serverSetting("session_timezone", "Europe/Zurich");

	...
}

⚠️ При установке параметров через метод setOptionClient.Builder или в классе настроек операции) имя настройки сервера должно иметь префикс clickhouse_setting_. В этом случае может быть полезен com.clickhouse.client.api.ClientConfigProperties#serverSetting().

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

Пользовательские HTTP-заголовки можно задать для всех операций (на уровне клиента) или для отдельной операции (на уровне операции).


QuerySettings settings = new QuerySettings()
    .httpHeader(HttpHeaders.REFERER, clientReferer)
    .setQueryId(qId);

Когда параметры задаются через метод setOptionClient.Builder или в классе настроек операции), имя пользовательского заголовка должно начинаться с префикса http_header_. Для этого может быть полезен метод com.clickhouse.client.api.ClientConfigProperties#httpHeader().

Общие определения

ClickHouseFormat

Перечисление поддерживаемых форматов. Включает все форматы, поддерживаемые ClickHouse.

  • raw - пользователь должен перекодировать сырые данные
  • full - клиент может самостоятельно выполнять транскодирование данных и принимает сырой поток данных
  • - — операция не поддерживается ClickHouse для данного формата

Эта версия клиента поддерживает:

ФорматВходные данныеВыходные данные
TabSeparatedrawбез изменений
TabSeparatedRawrawraw
TabSeparatedWithNamesнеобработанныеraw
TabSeparatedWithNamesAndTypesrawraw
TabSeparatedRawWithNamesrawraw
TabSeparatedRawWithNamesAndTypesrawraw
Templaterawraw
TemplateIgnoreSpacesraw*
CSVкак естькак есть
CSVWithNamesrawraw
CSVWithNamesAndTypesкак естьraw
CustomSeparatedrawraw
CustomSeparatedWithNamesнеобработанныйraw
CustomSeparatedWithNamesAndTypesrawraw
SQLInsert-как есть
Valuesrawraw
Vertical*без изменений
JSONrawбез изменений
JSONAsStringкак есть-
JSONAsObjectисходный*
JSONStringsкак естькак есть
JSONColumnsбез преобразованиякак есть
JSONColumnsWithMetadatarawкак есть
JSONCompactrawraw
JSONCompactStrings-как есть
JSONCompactColumnsrawкак есть
JSONEachRowкак естьraw
PrettyJSONEachRow*как есть
JSONEachRowWithProgress-как есть
JSONStringsEachRowrawкак есть
JSONStringsEachRowWithProgress*raw
JSONCompactEachRowкак естькак есть
JSONCompactEachRowWithNamesrawraw
JSONCompactEachRowWithNamesAndTypesrawкак есть
JSONCompactStringsEachRowrawraw
JSONCompactStringsEachRowWithNamesrawкак есть
JSONCompactStringsEachRowWithNamesAndTypesкак естькак есть
JSONObjectEachRowrawкак есть
BSONEachRowкак естькак есть
TSKVrawбез изменений
Pretty-raw
PrettyNoEscapes*raw
PrettyMonoBlock-raw
PrettyNoEscapesMonoBlock*raw
PrettyCompact-raw
PrettyCompactNoEscapes*raw
PrettyCompactMonoBlock-raw
PrettyCompactNoEscapesMonoBlock*как есть
PrettySpace-без изменений
PrettySpaceNoEscapes*raw
PrettySpaceMonoBlock-raw
PrettySpaceNoEscapesMonoBlock*без изменений
Prometheus-raw
Protobufrawraw
ProtobufSinglerawraw
ProtobufListнеобработанныйraw
Avrorawraw
AvroConfluentraw*
Parquetrawкак есть
ParquetMetadataraw-
Arrowrawбез изменений
ArrowStreamrawraw
ORCrawraw
Oneкак есть*
Npyнеобработанныйraw
RowBinaryfullfull
RowBinaryWithNamesfullполная
RowBinaryWithNamesAndTypesполнаяполный
RowBinaryWithDefaultsfull-
Nativefullнеобработанный
Null*raw
XML-raw
CapnProtorawraw
LineAsStringкак естьraw
Regexpraw*
RawBLOBrawraw
MsgPackrawкак есть
MySQLDumpraw-
DWARFraw*
Markdown-raw
Formкак есть*

API вставки

insert(String tableName, InputStream data, ClickHouseFormat format)

Принимает данные в виде InputStream байтов в указанном формате. Ожидается, что data закодированы в формате format.

Подписи

CompletableFuture<InsertResponse> insert(String tableName, InputStream data, ClickHouseFormat format, InsertSettings settings)
CompletableFuture<InsertResponse> insert(String tableName, InputStream data, ClickHouseFormat format)

Параметры

tableName — имя целевой таблицы.

data — входной поток кодированных данных.

format — формат кодирования данных.

settings — настройки запроса.

Возвращаемое значение

Future типа InsertResponse — результат операции и дополнительная информация, например, метрики на стороне сервера.

Примеры

try (InputStream dataStream = getDataStream()) {
    try (InsertResponse response = client.insert(TABLE_NAME, dataStream, ClickHouseFormat.JSONEachRow,
            insertSettings).get(3, TimeUnit.SECONDS)) {

        log.info("Insert finished: {} rows written", response.getMetrics().getMetric(ServerMetrics.NUM_ROWS_WRITTEN).getLong());
    } catch (Exception e) {
        log.error("Failed to write JSONEachRow data", e);
        throw new RuntimeException(e);
    }
}

insert(String tableName, List<?> data, InsertSettings settings)

Отправляет запрос на запись в базу данных. Список объектов преобразуется в эффективный формат, после чего отправляется на сервер. Класс элементов списка должен быть зарегистрирован заранее с помощью метода register(Class, TableSchema).

Подписи

client.insert(String tableName, List<?> data, InsertSettings settings)
client.insert(String tableName, List<?> data)

Параметры

tableName — имя целевой таблицы.

data — коллекция объектов DTO (объектов передачи данных).

settings — настройки запроса.

Возвращаемое значение

Future типа InsertResponse — результат операции и дополнительная информация, например, метрики на стороне сервера.

Примеры

// Important step (done once) - register class to pre-compile object serializer according to the table schema.
client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

List<ArticleViewEvent> events = loadBatch();

try (InsertResponse response = client.insert(TABLE_NAME, events).get()) {
    // handle response, then it will be closed and connection that served request will be released.
}

InsertSettings

Параметры конфигурации для операций INSERT.

Методы настройки

МетодОписание
setQueryId(String queryId)Устанавливает ID запроса, который будет присвоен операции. По умолчанию: null.
setDeduplicationToken(String token)Устанавливает токен дедупликации. Этот токен будет отправлен на сервер и может использоваться для идентификации запроса. Значение по умолчанию: null.
setInputStreamCopyBufferSize(int size)Размер буфера для копирования. Буфер используется во время операций записи для копирования данных из входного потока, предоставленного пользователем, в выходной поток. По умолчанию: 8196.
serverSetting(String name, String value)Задает отдельный параметр сервера для операции.
serverSetting(String name, Collection values)Устанавливает отдельные серверные настройки с несколькими значениями для операции. Элементы коллекции должны быть значениями типа String.
setDBRoles(Collection dbRoles)Устанавливает роли БД, которые будут применены перед выполнением операции. Элементы коллекции должны быть значениями типа String.
setOption(String option, Object value)Устанавливает параметр конфигурации в «сыром» виде. Это не серверная настройка.

InsertResponse

Объект ответа, содержащий результат операции INSERT. Доступен только в том случае, если клиент получил ответ от сервера.

Примечание

Этот объект необходимо закрыть как можно скорее для освобождения соединения, так как соединение не может быть использовано повторно до полного чтения всех данных предыдущего ответа.

МетодОписание
OperationMetrics getMetrics()Возвращает объект с метриками операции.
String getQueryId()Возвращает идентификатор запроса, назначенный операции приложением (через параметры операции) или сервером.

API запросов

query(String sqlQuery)

Отправляет sqlQuery как есть. Формат ответа задается настройками запроса. QueryResponse будет содержать ссылку на поток ответа, который должен быть обработан читателем для соответствующего формата.

Подписи

CompletableFuture<QueryResponse> query(String sqlQuery, QuerySettings settings)
CompletableFuture<QueryResponse> query(String sqlQuery)

Параметры

sqlQuery — один SQL-оператор. Запрос отправляется на сервер в исходном виде.

settings — настройки запроса.

Возвращаемое значение

Future типа QueryResponse — набор данных результата запроса и дополнительная информация, такая как метрики на стороне сервера. Объект Response должен быть закрыт после использования набора данных.

Примеры

final String sql = "select * from " + TABLE_NAME + " where title <> '' limit 10";

// Default format is RowBinaryWithNamesAndTypesFormatReader so reader have all information about columns
try (QueryResponse response = client.query(sql).get(3, TimeUnit.SECONDS);) {

    // Create a reader to access the data in a convenient way
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

    while (reader.hasNext()) {
        reader.next(); // Read the next record from stream and parse it

        // get values
        double id = reader.getDouble("id");
        String title = reader.getString("title");
        String url = reader.getString("url");

        // collecting data
    }
} catch (Exception e) {
    log.error("Failed to read data", e);
}

// put business logic outside of the reading block to release http connection asap.

query(String sqlQuery, Map<String, Object> queryParams, QuerySettings settings)

Отправляет sqlQuery как есть. Дополнительно отправляет параметры запроса, чтобы сервер мог скомпилировать SQL-выражение.

Подписи

CompletableFuture<QueryResponse> query(String sqlQuery, Map<String, Object> queryParams, QuerySettings settings)

Параметры

sqlQuery - SQL-выражение с плейсхолдерами {}.

queryParams - словарь переменных для подстановки в SQL-выражение на сервере.

settings — настройки запроса.

Возвращаемое значение

Future типа QueryResponse — набор данных результата запроса и дополнительная информация, такая как метрики на стороне сервера. Объект Response должен быть закрыт после использования набора данных.

Примеры


// define parameters. They will be sent to the server along with the request.
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("param1", 2);

try (QueryResponse response =
        client.query("SELECT * FROM " + table + " WHERE col1 >= {param1:UInt32}", queryParams, new QuerySettings()).get()) {

    // Create a reader to access the data in a convenient way
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);

    while (reader.hasNext()) {
        reader.next(); // Read the next record from stream and parse it

        // reading data
    }

} catch (Exception e) {
    log.error("Failed to read data", e);
}

queryAll(String sqlQuery)

Запрашивает данные в формате RowBinaryWithNamesAndTypes. Возвращает результат в виде коллекции. Производительность чтения такая же, как при использовании reader, но требуется больше памяти для хранения всего набора данных.

Подписи

List<GenericRecord> queryAll(String sqlQuery)

Параметры

sqlQuery — SQL-выражение для выполнения запроса данных с сервера.

Возвращаемое значение

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

Примеры

try {
    log.info("Reading whole table and process record by record");
    final String sql = "select * from " + TABLE_NAME + " where title <> ''";

    // Read whole result set and process it record by record
    client.queryAll(sql).forEach(row -> {
        double id = row.getDouble("id");
        String title = row.getString("title");
        String url = row.getString("url");

        log.info("id: {}, title: {}, url: {}", id, title, url);
    });
} catch (Exception e) {
    log.error("Failed to read data", e);
}

QuerySettings

Параметры конфигурации для операций с запросами.

Методы настройки

МетодОписание
setQueryId(String queryId)Задает идентификатор запроса, который будет присвоен операции.
setFormat(ClickHouseFormat format)Задаёт формат ответа. Полный список см. в RowBinaryWithNamesAndTypes.
setMaxExecutionTime(Integer maxExecutionTime)Устанавливает максимальное время выполнения операции на сервере. Не влияет на таймаут чтения.
waitEndOfQuery(Boolean waitEndOfQuery)Указывает серверу дождаться завершения запроса перед отправкой ответа.
setUseServerTimeZone(Boolean useServerTimeZone)Часовой пояс сервера (см. конфигурацию клиента) будет использоваться при разборе типов даты/времени в результате операции. По умолчанию false.
setUseTimeZone(String timeZone)Запрашивает у сервера использование значения timeZone для преобразования времени. См. session_timezone.
serverSetting(String name, String value)Задаёт отдельную настройку сервера для операции.
serverSetting(String name, Collection values)Задает отдельные параметры сервера с несколькими значениями для операции. Элементы коллекции должны иметь тип String.
setDBRoles(Collection dbRoles)Указывает роли БД, которые нужно назначить перед выполнением операции. Элементы коллекции должны иметь тип String.
setOption(String option, Object value)Устанавливает параметр конфигурации в необработанном виде. Это не параметр сервера.

QueryResponse

Объект ответа, содержащий результат выполнения запроса. Доступен только при получении ответа от сервера.

Примечание

Этот объект необходимо закрыть как можно скорее для освобождения соединения, так как соединение не может быть использовано повторно до полного чтения всех данных предыдущего ответа.

МетодОписание
ClickHouseFormat getFormat()Возвращает формат, в котором закодированы данные ответа.
InputStream getInputStream()Возвращает поток несжатых байтов данных в заданном формате.
OperationMetrics getMetrics()Возвращает объект с метриками операции.
String getQueryId()Возвращает идентификатор запроса для операции, назначенный приложением (через настройки операции) или сервером.
TimeZone getTimeZone()Возвращает часовой пояс, который должен использоваться при обработке типов Date/DateTime в ответе.

Примеры

Общий API

getTableSchema(String table)

Получает схему таблицы table.

Подписи

TableSchema getTableSchema(String table)
TableSchema getTableSchema(String table, String database)

Параметры

table — имя таблицы, для которой требуется получить данные схемы.

database — база данных, где определена целевая таблица.

Возвращаемое значение

Возвращает объект TableSchema со списком столбцов таблицы.

getTableSchemaFromQuery(String sql)

Получает схему из SQL-выражения.

Подписи

TableSchema getTableSchemaFromQuery(String sql)

Параметры

sql - SQL-оператор "SELECT", для которого должна быть возвращена схема.

Возвращаемое значение

Возвращает объект TableSchema со столбцами, соответствующими SQL-выражению sql.

TableSchema

register(Class<?> clazz, TableSchema schema)

Компилирует слой сериализации и десериализации для Java-класса, используемого для записи и чтения данных с помощью schema. Метод создаёт сериализатор и десериализатор для пары getter/setter и соответствующего столбца. Соответствие столбца находится путём извлечения его имени из имени метода. Например, getFirstName будет соответствовать столбцу first_name или firstname.

Подписи

void register(Class<?> clazz, TableSchema schema)

Параметры

clazz - Класс, представляющий POJO для чтения и записи данных.

schema - Схема данных, используемая для сопоставления со свойствами POJO.

Примеры

client.register(ArticleViewEvent.class, client.getTableSchema(TABLE_NAME));

Примеры использования

Полные примеры кода хранятся в репозитории в папке example:

  • client-v2 - основной набор примеров.
  • demo-service - пример того, как использовать клиента в приложении Spring Boot.
  • demo-kotlin-service - пример того, как использовать клиент в приложении на Ktor (Kotlin).

Руководство по миграции

Старый клиент (V1) использовал com.clickhouse.client.ClickHouseClient#builder в качестве отправной точки. Новый клиент (V2) использует аналогичный паттерн с com.clickhouse.client.api.Client.Builder. Основные отличия:

  • загрузчик сервисов (service loader) не используется для выбора реализации. Класс com.clickhouse.client.api.Client является фасадом для всех вариантов реализаций в будущем.
  • меньше источников конфигурации: одна передаётся билдеру и одна задаётся настройками операций (QuerySettings, InsertSettings). В предыдущей версии конфигурация задавалась на уровне каждого узла и в некоторых случаях загружала переменные окружения.

Соответствие параметров конфигурации

В V1 имеется 3 класса перечислений, связанных с конфигурацией:

  • com.clickhouse.client.config.ClickHouseDefaults — параметры конфигурации, которые обычно задаются в большинстве случаев. Например, USER и PASSWORD.
  • com.clickhouse.client.config.ClickHouseClientOption — параметры конфигурации, относящиеся к клиенту. Например, HEALTH_CHECK_INTERVAL.
  • com.clickhouse.client.http.config.ClickHouseHttpOption — конфигурационные параметры, специфичные для HTTP-интерфейса. Например, RECEIVE_QUERY_PROGRESS.

Они были разработаны для группировки параметров и обеспечения четкого разделения. Однако в некоторых случаях это приводило к путанице (есть ли разница между com.clickhouse.client.config.ClickHouseDefaults#ASYNC и com.clickhouse.client.config.ClickHouseClientOption#ASYNC). Новый клиент V2 использует com.clickhouse.client.api.Client.Builder в качестве единого словаря всех возможных параметров конфигурации клиента. Существует com.clickhouse.client.api.ClientConfigProperties, где перечислены все имена параметров конфигурации.

В таблице ниже показано, какие старые параметры поддерживаются в новом клиенте и каково их новое значение.

Условные обозначения: ✔ = поддерживается, ✗ = не поддерживается

Конфигурация V1Метод билдера V2Комментарии
ClickHouseDefaults#HOSTClient.Builder#addEndpoint
ClickHouseDefaults#PROTOCOLВ V2 поддерживается только HTTP-протокол
ClickHouseDefaults#DATABASE
ClickHouseClientOption#DATABASE
Client.Builder#setDefaultDatabase
ClickHouseDefaults#USERClient.Builder#setUsername
ClickHouseDefaults#PASSWORDClient.Builder#setPassword
ClickHouseClientOption#CONNECTION_TIMEOUTClient.Builder#setConnectTimeout
ClickHouseClientOption#CONNECTION_TTLClient.Builder#setConnectionTTL
ClickHouseHttpOption#MAX_OPEN_CONNECTIONSClient.Builder#setMaxConnections
ClickHouseHttpOption#KEEP_ALIVE
ClickHouseHttpOption#KEEP_ALIVE_TIMEOUT
Client.Builder#setKeepAliveTimeout
ClickHouseHttpOption#CONNECTION_REUSE_STRATEGYClient.Builder#setConnectionReuseStrategy
ClickHouseHttpOption#USE_BASIC_AUTHENTICATIONClient.Builder#useHTTPBasicAuth

Общие отличия

  • Клиент V2 использует меньше проприетарных классов, что повышает портируемость. Например, V2 работает с любой реализацией java.io.InputStream для записи данных на сервер.
  • Настройка async в Client V2 по умолчанию отключена (off). Это означает отсутствие дополнительных потоков и больший контроль приложения над клиентом. В большинстве случаев эта настройка должна оставаться off. Включение async создаст отдельный поток для запроса. Это имеет смысл только при использовании управляемого приложением executor (см. com.clickhouse.client.api.Client.Builder#setSharedOperationExecutor)

Запись данных

  • Используйте любую реализацию интерфейса java.io.InputStream. Версия V1 com.clickhouse.data.ClickHouseInputStream поддерживается, но НЕ рекомендуется к использованию.
  • После обнаружения конца входного потока он обрабатывается соответствующим образом. Перед этим должен быть закрыт выходной поток запроса.

V1 Вставка данных в формате TSV.

InputStream inData = getInData();
ClickHouseRequest.Mutation request = client.read(server)
        .write()
        .table(tableName)
        .format(ClickHouseFormat.TSV);
ClickHouseConfig config = request.getConfig();
CompletableFuture<ClickHouseResponse> future;
try (ClickHousePipedOutputStream requestBody = ClickHouseDataStreamFactory.getInstance()
        .createPipedOutputStream(config)) {
    // start the worker thread which transfer data from the input into ClickHouse
    future = request.data(requestBody.getInputStream()).execute();

    // Copy data from inData stream to requestBody stream

    // We need to close the stream before getting a response
    requestBody.close();

    try (ClickHouseResponse response = future.get()) {
        ClickHouseResponseSummary summary = response.getSummary();
        Assert.assertEquals(summary.getWrittenRows(), numRows, "Num of written rows");
    }
}

V2 Вставка данных в формате TSV.

InputStream inData = getInData();
InsertSettings settings = new InsertSettings().setInputStreamCopyBufferSize(8198 * 2); // set copy buffer size
try (InsertResponse response = client.insert(tableName, inData, ClickHouseFormat.TSV, settings).get(30, TimeUnit.SECONDS)) {

  // Insert is complete at this point

} catch (Exception e) {
 // Handle exception
}
  • достаточно вызвать один метод. Нет необходимости создавать дополнительный объект запроса.
  • Поток тела запроса автоматически закрывается после копирования всех данных.
  • Теперь доступен новый низкоуровневый API com.clickhouse.client.api.Client#insert(java.lang.String, java.util.List<java.lang.String>, com.clickhouse.client.api.DataStreamWriter, com.clickhouse.data.ClickHouseFormat, com.clickhouse.client.api.insert.InsertSettings). com.clickhouse.client.api.DataStreamWriter предназначен для реализации пользовательской логики записи данных, например для чтения данных из очереди.

Чтение данных

  • По умолчанию данные читаются в формате RowBinaryWithNamesAndTypes. В настоящий момент при необходимости привязки данных поддерживается только этот формат.
  • Данные можно считывать как коллекцию записей с помощью метода List<GenericRecord> com.clickhouse.client.api.Client#queryAll(java.lang.String). Он загружает данные в память и освобождает соединение, поэтому дополнительная обработка не требуется. GenericRecord предоставляет доступ к данным и реализует некоторые преобразования.
Collection<GenericRecord> records = client.queryAll("SELECT * FROM table");
for (GenericRecord record : records) {
    int rowId = record.getInteger("rowID");
    String name = record.getString("name");
    LocalDateTime ts = record.getLocalDateTime("ts");
}

Клиентская библиотека Java для взаимодействия с сервером БД через его протоколы. Текущая реализация поддерживает только HTTP-интерфейс. Библиотека предоставляет собственный API для отправки запросов на сервер.

Устаревание

Эта библиотека скоро будет объявлена устаревшей. Для новых проектов используйте последнюю версию Java Client

Настройка

<!-- https://mvnrepository.com/artifact/com.clickhouse/clickhouse-http-client -->
<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>clickhouse-http-client</artifactId>
    <version>0.7.2</version>
</dependency>

Начиная с версии 0.5.0, драйвер использует новую клиентскую HTTP-библиотеку, которую необходимо добавить как зависимость.

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents.client5/httpclient5 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3.1</version>
</dependency>

Инициализация

Формат URL-адреса подключения: protocol://host[:port][/database][?param[=value][&param[=value]][#tag[,tag]], например:

  • http://localhost:8443?ssl=true&sslmode=NONE
  • https://(https://explorer@play.clickhouse.com:443

Подключитесь к одному узлу:

ClickHouseNode server = ClickHouseNode.of("http://localhost:8123/default?compress=0");

Подключение к кластеру с несколькими узлами:

ClickHouseNodes servers = ClickHouseNodes.of(
    "jdbc:ch:http://server1.domain,server2.domain,server3.domain/my_db"
    + "?load_balancing_policy=random&health_check_interval=5000&failover=2");

API запросов

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            long totalRows = summary.getTotalRowsToRead();
}

API потоковых запросов

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from numbers limit :limit")
        .params(1000)
        .executeAndWait()) {
            for (ClickHouseRecord r : response.records()) {
            int num = r.getValue(0).asInteger();
            // type conversion
            String str = r.getValue(0).asString();
            LocalDate date = r.getValue(0).asDate();
        }
}

См. полный пример кода в репозитории.

API вставки


try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers).write()
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("insert into my_table select c2, c3 from input('c1 UInt8, c2 String, c3 Int32')")
        .data(myInputStream) // `myInputStream` is source of data in RowBinary format
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

См. полный пример кода в репозитории.

Кодирование RowBinary

Формат RowBinary описан на его странице.

Пример доступен в коде.

Функциональные возможности

Сжатие

По умолчанию клиент использует сжатие LZ4, что требует следующей зависимости:

<!-- https://mvnrepository.com/artifact/org.lz4/lz4-java -->
<dependency>
    <groupId>org.lz4</groupId>
    <artifactId>lz4-java</artifactId>
    <version>1.8.0</version>
</dependency>

Вы можете использовать gzip, указав compress_algorithm=gzip в URL подключения.

Также можно отключить сжатие несколькими способами.

  1. Отключите, указав compress=0 в URL подключения: http://localhost:8123/default?compress=0
  2. Отключите в конфигурации клиента:
ClickHouseClient client = ClickHouseClient.builder()
   .config(new ClickHouseConfig(Map.of(ClickHouseClientOption.COMPRESS, false)))
   .nodeSelector(ClickHouseNodeSelector.of(ClickHouseProtocol.HTTP))
   .build();

См. документацию по сжатию для получения дополнительной информации о различных параметрах сжатия.

Несколько запросов

Выполните несколько запросов в рабочем потоке последовательно в рамках одной сессии:

CompletableFuture<List<ClickHouseResponseSummary>> future = ClickHouseClient.send(servers.apply(servers.getNodeSelector()),
    "create database if not exists my_base",
    "use my_base",
    "create table if not exists test_table(s String) engine=Memory",
    "insert into test_table values('1')('2')('3')",
    "select * from test_table limit 1",
    "truncate table test_table",
    "drop table if exists test_table");
List<ClickHouseResponseSummary> results = future.get();

Именованные параметры

Параметры можно передавать по имени, не полагаясь исключительно на их позицию в списке параметров. Эта возможность доступна при использовании функции params.

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name limit :limit")
        .params("Ben", 1000)
        .executeAndWait()) {
            //...
        }
}
Параметры

Все сигнатуры params, включающие тип String (String, String[], Map<String, String>), предполагают, что передаваемые ключи являются валидными SQL-строками ClickHouse. Например:

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name")
        .params(Map.of("name","'Ben'"))
        .executeAndWait()) {
            //...
        }
}

Если вы не хотите вручную преобразовывать объекты String в ClickHouse SQL, можно использовать вспомогательную функцию ClickHouseValues.convertToSqlExpression, расположенную в com.clickhouse.data:

try (ClickHouseClient client = ClickHouseClient.newInstance(ClickHouseProtocol.HTTP);
     ClickHouseResponse response = client.read(servers)
        .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
        .query("select * from my_table where name=:name")
        .params(Map.of("name", ClickHouseValues.convertToSqlExpression("Ben's")))
        .executeAndWait()) {
            //...
        }
}

В приведённом выше примере ClickHouseValues.convertToSqlExpression экранирует внутреннюю одинарную кавычку и заключает переменную в корректные одинарные кавычки.

Другие типы данных, такие как Integer, UUID, Array и Enum, будут автоматически преобразованы внутри params.

Обнаружение узлов

Java-клиент предоставляет возможность автоматического обнаружения узлов ClickHouse. Автообнаружение отключено по умолчанию. Чтобы включить его вручную, установите auto_discovery в true:

properties.setProperty("auto_discovery", "true");

Или в URL-адресе подключения:

jdbc:ch://my-server/system?auto_discovery=true

При включенном автообнаружении нет необходимости указывать все узлы ClickHouse в URL подключения. Узлы, указанные в URL, будут использоваться в качестве начальных, и Java-клиент автоматически обнаружит остальные узлы из системных таблиц и/или clickhouse-keeper или zookeeper.

За настройку автоматического обнаружения отвечают следующие параметры:

ПараметрЗначение по умолчаниюОписание
auto_discoveryfalseДолжен ли клиент обнаруживать дополнительные узлы по системным таблицам и/или через clickhouse-keeper/zookeeper.
node_discovery_interval0Интервал обнаружения узлов в миллисекундах; нулевое или отрицательное значение означает однократное обнаружение.
node_discovery_limit100Максимальное количество узлов, которые могут быть обнаружены одновременно; нулевое или отрицательное значение означает отсутствие ограничения.

Балансировка нагрузки

Java-клиент выбирает узел ClickHouse для отправки запросов согласно политике балансировки нагрузки. Политика балансировки нагрузки отвечает за следующее:

  1. Получить узел из списка управляемых узлов.
  2. Управление статусом узла.
  3. При необходимости настройте фоновый процесс обнаружения узлов (если включено автообнаружение) и выполните проверку состояния.

Список параметров для настройки балансировки нагрузки:

СвойствоЗначение по умолчаниюОписание
load_balancing_policy""Политика балансировки нагрузки может иметь одно из следующих значений:
  • firstAlive - запрос отправляется первому работоспособному узлу из списка управляемых узлов
  • random - запрос отправляется случайному узлу из списка управляемых узлов
  • roundRobin - запросы по очереди отправляются каждому узлу из списка управляемых узлов
  • полностью квалифицированное имя класса, реализующего ClickHouseLoadBalancingPolicy — пользовательская политика балансировки нагрузки
  • Если она не указана, запрос отправляется на первый узел из списка управляемых узлов
    load_balancing_tags""Теги балансировки нагрузки для фильтрации узлов. Запросы отправляются только на узлы с указанными тегами
    health_check_interval0Интервал проверки работоспособности в миллисекундах: нулевое или отрицательное значение означает однократную проверку.
    health_check_methodClickHouseHealthCheckMethod.SELECT_ONEМетод проверки работоспособности. Может быть одним из следующих:
  • ClickHouseHealthCheckMethod.SELECT_ONE - проверка запросом select 1
  • ClickHouseHealthCheckMethod.PING - проверка на уровне протокола, как правило, более быстрая
  • node_check_interval0Интервал проверки узла в миллисекундах, отрицательное значение интерпретируется как ноль. Статус узла проверяется, если с момента последней проверки прошло указанное количество времени.
    Разница между health_check_interval и node_check_interval заключается в том, что опция health_check_interval планирует фоновое задание, которое проверяет статус для списка узлов (всех или только проблемных), а node_check_interval задаёт интервал, который должен пройти с момента последней проверки конкретного узла.
    check_all_nodesfalseВыполнять проверку работоспособности для всех узлов или только для проблемных узлов.

    Отказоустойчивость и повторные попытки

    Java-клиент предоставляет параметры конфигурации для настройки отказоустойчивости и повторных попыток выполнения неудачных запросов:

    ПараметрЗначение по умолчаниюОписание
    переключение при отказе0Максимальное количество переключений при обработке запроса. Нулевое или отрицательное значение означает, что переключение не выполняется. При переключении неуспешный запрос отправляется на другой узел (в соответствии с политикой балансировки нагрузки) для восстановления после отказа.
    retry0Максимальное число повторных попыток для одного запроса. Нулевое или отрицательное значение означает отсутствие повторных попыток. Повторный запрос отправляется на тот же узел и выполняется только в том случае, если сервер ClickHouse возвращает код ошибки NETWORK_ERROR
    repeat_on_session_locktrueНужно ли повторять выполнение, если сеанс заблокирован, до истечения времени ожидания (согласно session_timeout или connect_timeout). Неудачный запрос повторяется, если сервер ClickHouse возвращает код ошибки SESSION_IS_LOCKED

    Добавление пользовательских HTTP-заголовков

    Java-клиент поддерживает транспортный уровень HTTP/S для добавления пользовательских HTTP-заголовков к запросу. Используйте свойство custom_http_headers. Заголовки должны быть разделены запятой ,, а ключ и значение заголовка — знаком равенства =.

    Поддержка Java-клиента

    options.put("custom_http_headers", "X-ClickHouse-Quota=test, X-ClickHouse-Test=test");
    

    Драйвер JDBC

    properties.setProperty("custom_http_headers", "X-ClickHouse-Quota=test, X-ClickHouse-Test=test");