본문으로 바로가기
본문으로 바로가기

Java 클라이언트

프로토콜을 통해 데이터베이스 서버와 통신하는 Java 클라이언트 라이브러리입니다. 현재 구현은 HTTP 인터페이스만 지원합니다. 이 라이브러리는 서버에 요청을 전송하기 위한 자체 API를 제공합니다. 또한 다양한 바이너리 데이터 형식(RowBinary* 및 Native*)을 처리할 수 있는 도구도 제공합니다.

설정


<dependency>
    <groupId>com.clickhouse</groupId>
    <artifactId>client-v2</artifactId>
    <version>0.9.6</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();

ClientAutoCloseable이며 더 이상 필요하지 않을 경우 닫아야 합니다.

인증

인증은 초기화 단계에서 클라이언트별로 구성됩니다. 지원되는 인증 방법은 세 가지입니다: 비밀번호, 액세스 토큰(Access Token), SSL 클라이언트 인증서(SSL Client Certificate).

비밀번호 인증을 사용하려면 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 클라이언트 인증서를 통한 인증을 사용하려면 setUsername(String), useSSLAuthentication(boolean), setClientCertificate(String), setClientKey(String)을 각각 호출하여 사용자 이름을 설정하고, SSL 인증을 활성화하며, 클라이언트 인증서와 클라이언트 키를 설정해야 합니다:

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 형식의 서버 주소사용 가능한 서버 목록에 서버 엔드포인트를 추가합니다. 현재는 하나의 엔드포인트만 지원됩니다.nonenone
addEndpoint(Protocol protocol, String host, int port, boolean secure)protocol - 연결 프로토콜
host - IP 또는 호스트 이름
secure - HTTPS 사용
사용 가능한 서버 목록에 서버 엔드포인트를 추가합니다. 현재는 하나의 엔드포인트만 지원됩니다.nonenone
enableConnectionPool(boolean enable)enable - 활성/비활성 플래그커넥션 풀을 사용할지 여부를 설정합니다.trueconnection_pool_enabled
setMaxConnections(int maxConnections)maxConnections - 커넥션 개수클라이언트가 각 서버 엔드포인트에 대해 열 수 있는 최대 커넥션 수를 설정합니다.10max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit)timeout - 타임아웃 값
unit - 시간 단위
커넥션이 비활성으로 간주되는 TTL을 설정합니다.-1connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit)timeout - 타임아웃 값
unit - 시간 단위
HTTP 커넥션 Keep-Alive 타임아웃을 설정합니다. Keep-Alive를 비활성화하려면 0으로 설정합니다.-http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy)strategy - LIFO 또는 FIFO커넥션 풀이 사용할 커넥션 재사용 전략을 선택합니다.FIFOconnection_reuse_strategy
setDefaultDatabase(String database)database - 데이터베이스 이름기본 데이터베이스를 설정합니다.defaultdatabase

서버 설정

서버 측 설정은 클라이언트 생성 시 한 번 설정할 수 있으며(BuilderserverSetting 메서드 참조), 작업 수준에서도 설정할 수 있습니다(작업 설정 클래스의 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");

	...
}

⚠️ setOption 메서드(Client.Builder 또는 작업 설정 클래스)를 통해 옵션을 설정하는 경우, 서버 설정 이름 앞에 clickhouse_setting_ 접두사를 붙여야 합니다. 이 경우 com.clickhouse.client.api.ClientConfigProperties#serverSetting()을 사용하면 유용합니다.

커스텀 HTTP 헤더

사용자 정의 HTTP 헤더는 모든 작업(클라이언트 수준)에 대해 설정하거나 개별 작업(작업 수준)에 대해 설정할 수 있습니다.


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

setOption 메서드(Client.Builder 또는 작업 설정 클래스)를 통해 옵션을 설정하는 경우, 사용자 정의 헤더 이름 앞에 http_header_ 접두사를 붙여야 합니다. 이 경우 com.clickhouse.client.api.ClientConfigProperties#httpHeader() 메서드가 유용합니다.

공통 정의

ClickHouseFormat

지원되는 형식의 열거형(Enum)입니다. ClickHouse가 지원하는 모든 형식을 포함합니다.

  • raw - 원시 데이터를 트랜스코딩해야 합니다
  • full - 클라이언트가 자체적으로 데이터를 트랜스코딩할 수 있으며 원시 데이터 스트림을 그대로 수신합니다
  • - - 이 형식에서는 ClickHouse에서 해당 연산을 지원하지 않습니다

이 클라이언트 버전은 다음을 지원합니다:

형식입력출력
TabSeparated원시원시
TabSeparatedRaw원시원시
TabSeparatedWithNames원시원시
TabSeparatedWithNamesAndTypes원시원시
TabSeparatedRawWithNames원시원시
TabSeparatedRawWithNamesAndTypes원시원시
Template원시원시
TemplateIgnoreSpaces원시*
CSV원시원시
CSVWithNames원시원시
CSVWithNamesAndTypes원시원시
CustomSeparated원시원시
CustomSeparatedWithNames원시원시
CustomSeparatedWithNamesAndTypes원시원시
SQLInsert-원시
Values원시원시
Vertical*원시
JSON원시원시
JSONAsString원시-
JSONAsObject원시*
JSONStrings원시원시
JSONColumns원시원시
JSONColumnsWithMetadata원시원시
JSONCompact원시원시
JSONCompactStrings-원시
JSONCompactColumns원시원시 형식
JSONEachRow원시 형식원시 형식
PrettyJSONEachRow*원시 형식
JSONEachRowWithProgress-원시 형식
JSONStringsEachRow원시 형식원시 형식
JSONStringsEachRowWithProgress*원시 형식
JSONCompactEachRow원시 형식원시 형식
JSONCompactEachRowWithNames원시 형식원시 형식
JSONCompactEachRowWithNamesAndTypes원시 형식원시 형식
JSONCompactStringsEachRow원시 형식원시 형식
JSONCompactStringsEachRowWithNames원시 형식원시 형식
JSONCompactStringsEachRowWithNamesAndTypes원시 형식원시 형식
JSONObjectEachRow원시 형식원시 형식
BSONEachRow원시 형식원시 형식
TSKV원시 형식원시 형식
Pretty-원시 형식
PrettyNoEscapes*원시 형식
PrettyMonoBlock-원시 형식
PrettyNoEscapesMonoBlock*원시 형식
PrettyCompact-원시 형식
PrettyCompactNoEscapes*원시 형식
PrettyCompactMonoBlock-원시 형식
PrettyCompactNoEscapesMonoBlock*원시 형식
PrettySpace-원시 형식
PrettySpaceNoEscapes*원시 형식
PrettySpaceMonoBlock-원시 형식
PrettySpaceNoEscapesMonoBlock*원시 형식
Prometheus-원시
Protobuf원시원시
ProtobufSingle원시원시
ProtobufList원시원시
Avro원시원시
AvroConfluent원시*
Parquet원시원시
ParquetMetadata원시-
Arrow원시원시
ArrowStream원시원시
ORC원시원시
One원시*
Npy원시원시
RowBinary전체전체
RowBinaryWithNames전체전체
RowBinaryWithNamesAndTypes전체전체
RowBinaryWithDefaults전체-
Native전체원시
Null*원시
XML-원시
CapnProto원시원시
LineAsString원시원시
Regexp원시*
RawBLOB원시원시
MsgPack원시원시
MySQLDump원시-
DWARF원시*
Markdown-원본
양식원본*

Insert API

insert(String tableName, InputStream data, ClickHouseFormat format)

지정된 형식의 바이트 InputStream으로 데이터를 수신합니다. dataformat으로 인코딩되어 있어야 합니다.

시그니처

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

파라미터

tableName - 대상 테이블 이름.

data - 인코딩된 데이터의 입력 스트림입니다.

format - 데이터가 인코딩되는 형식입니다.

settings - 요청 설정.

반환값

InsertResponse 타입의 Future - 작업 결과 및 서버 측 메트릭과 같은 추가 정보를 포함합니다.

예시

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(Data Transfer Object) 객체 컬렉션입니다.

settings - 요청 설정.

반환값

InsertResponse 타입의 Future - 작업 결과 및 서버 측 메트릭과 같은 추가 정보를 포함합니다.

예시

// 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

삽입 작업에 대한 구성 옵션입니다.

구성 방법

메서드설명
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)작업을 실행하기 전에 적용할 DB 역할을 설정합니다. 컬렉션의 항목은 String 값이어야 합니다.
setOption(String option, Object value)구성 옵션을 원시(raw) 형식으로 설정합니다. 이 옵션은 서버 설정이 아닙니다.

InsertResponse

삽입 작업의 결과를 보유하는 응답 객체입니다. 클라이언트가 서버로부터 응답을 받은 경우에만 사용 가능합니다.

참고

이전 응답의 모든 데이터를 완전히 읽기 전까지는 연결을 재사용할 수 없으므로, 연결을 해제하기 위해 이 객체를 가능한 한 빨리 닫으십시오.

메서드설명
OperationMetrics getMetrics()작업 메트릭 객체를 반환합니다.
String getQueryId()애플리케이션에서(작업 구성 또는 서버를 통해) 해당 작업에 할당한 쿼리 ID를 반환합니다.

쿼리 API

query(String sqlQuery)

sqlQuery를 있는 그대로 전송합니다. 응답 형식은 쿼리 설정에 의해 지정됩니다. QueryResponse는 해당 형식을 지원하는 리더에서 소비해야 하는 응답 스트림에 대한 참조를 보유합니다.

시그니처

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

파라미터

sqlQuery - 단일 SQL 문입니다. 쿼리는 그대로 서버로 전송됩니다.

settings - 요청 설정.

반환값

QueryResponse 타입의 Future - 결과 데이터셋과 서버 측 메트릭 같은 추가 정보를 포함합니다. 데이터셋 사용 후 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 - 요청 설정.

반환값

QueryResponse 타입의 Future - 결과 데이터셋과 서버 측 메트릭 같은 추가 정보를 포함합니다. 데이터셋 사용 후 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 형식의 데이터를 쿼리합니다. 결과는 컬렉션으로 반환됩니다. 읽기 성능은 리더와 동일하지만, 전체 데이터셋을 메모리에 보관하기 위해 더 많은 메모리가 필요합니다.

시그니처

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)작업에 할당할 쿼리 ID를 설정합니다.
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)작업을 실행하기 전에 설정할 DB 역할을 지정합니다. 컬렉션의 각 항목은 String 값이어야 합니다.
setOption(String option, Object value)원시 형식으로 구성 옵션을 설정합니다. 서버 설정은 아닙니다.

QueryResponse

쿼리 실행 결과를 담고 있는 응답 객체입니다. 클라이언트가 서버로부터 응답을 받은 경우에만 사용 가능합니다.

참고

이전 응답의 모든 데이터를 완전히 읽기 전까지는 연결을 재사용할 수 없으므로, 연결을 해제하기 위해 이 객체를 가능한 한 빨리 닫으십시오.

메서드설명
ClickHouseFormat getFormat()응답 데이터가 인코딩되는 포맷을 반환합니다.
InputStream getInputStream()지정된 포맷의 비압축 데이터 바이트 스트림을 반환합니다.
OperationMetrics getMetrics()작업 메트릭을 포함하는 객체를 반환합니다.
String getQueryId()애플리케이션에서(작업 설정 또는 서버를 통해) 작업에 할당한 쿼리 ID를 반환합니다.
TimeZone getTimeZone()응답의 Date/DateTime 타입을 처리할 때 사용할 타임존을 반환합니다.

예시

  • 예제 코드는 저장소에서 확인할 수 있습니다.
  • Spring 서비스 구현을 참고하십시오.

공통 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 - 스키마를 반환해야 하는 "SELECT" SQL 문입니다.

반환값

sql 표현식과 일치하는 컬럼이 포함된 TableSchema 객체를 반환합니다.

TableSchema

register(Class<?> clazz, TableSchema schema)

Java 클래스가 schema를 사용하여 데이터를 쓰고 읽을 수 있도록 직렬화 및 역직렬화 레이어를 컴파일합니다. 이 메서드는 getter/setter 쌍과 해당 컬럼에 대한 직렬화기 및 역직렬화기를 생성합니다. 컬럼 매칭은 메서드 이름에서 컬럼 이름을 추출하여 수행됩니다. 예를 들어, getFirstNamefirst_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를 사용하여 유사한 패턴을 따릅니다. 주요 차이점은 다음과 같습니다:

  • 구현을 로드하기 위해 서비스 로더를 사용하지 않습니다. com.clickhouse.client.api.Client는 향후 모든 종류의 구현을 포괄하는 파사드 클래스입니다.
  • 구성 소스의 수가 줄었습니다. 하나는 빌더에 제공되고, 다른 하나는 동작 설정(QuerySettings, InsertSettings)으로 제공됩니다. 이전 버전에서는 노드별로 구성이 있었고, 일부 경우에는 환경 변수를 로드하고 있었습니다.

구성 매개변수 일치 확인

V1에는 구성과 관련된 3개의 enum 클래스가 있습니다:

  • com.clickhouse.client.config.ClickHouseDefaults - 대부분의 사용 사례에서 설정하도록 되어 있는 구성 매개변수입니다. 예를 들어 USERPASSWORD 등이 있습니다.
  • com.clickhouse.client.config.ClickHouseClientOption - 클라이언트 전용 구성 매개변수입니다. 예를 들어 HEALTH_CHECK_INTERVAL 등이 있습니다.
  • com.clickhouse.client.http.config.ClickHouseHttpOption - HTTP 인터페이스 전용 구성 매개변수입니다. 예: RECEIVE_QUERY_PROGRESS.

이들은 매개변수를 그룹화하고 명확하게 분리하도록 설계되었습니다. 그러나 일부 경우 혼란을 초래했습니다(com.clickhouse.client.config.ClickHouseDefaults#ASYNCcom.clickhouse.client.config.ClickHouseClientOption#ASYNC 사이에 차이가 있습니까?). 새로운 V2 클라이언트는 com.clickhouse.client.api.Client.Builder를 모든 클라이언트 구성 옵션을 담은 단일 딕셔너리로 사용합니다. 모든 구성 매개변수 이름은 com.clickhouse.client.api.ClientConfigProperties에 나열되어 있습니다.

아래 표는 새 클라이언트에서 지원되는 이전 옵션과 새로운 의미를 보여줍니다.

범례: ✔ = 지원됨, ✗ = 지원되지 않음

V1 설정V2 Builder 메서드설명
ClickHouseDefaults#HOSTClient.Builder#addEndpoint
ClickHouseDefaults#PROTOCOLV2에서는 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의 모든 구현체와 함께 동작합니다.
  • Client V2의 async 설정은 기본적으로 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
}
  • 호출해야 하는 메서드는 하나뿐입니다. 추가적인 요청 객체를 만들 필요가 없습니다.
  • 모든 데이터가 복사되면 request body 스트림은 자동으로 닫힙니다.
  • 새로운 저수준 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();
        }
}

저장소에서 전체 코드 예제를 참조하세요.

Insert 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>

연결 URL에서 compress_algorithm=gzip으로 설정하여 gzip을 대신 사용하실 수 있습니다.

또는 다음과 같은 몇 가지 방법으로 압축을 비활성화할 수 있습니다.

  1. 연결 URL에 compress=0을 설정하여 압축을 비활성화합니다: 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();

다양한 압축 옵션에 대한 자세한 내용은 압축 문서를 참조하세요.

여러 쿼리

동일한 세션 내에서 워커 스레드(worker thread)에서 여러 쿼리를 순차적으로 실행하세요:

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();

명명된 매개변수(Named Parameters)

매개변수 목록에서 위치에만 의존하지 않고 이름으로 매개변수를 전달할 수 있습니다. 이 기능은 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()) {
            //...
        }
}
매개변수

String 타입(String, String[], Map<String, String>)과 관련된 모든 params 시그니처는 전달되는 키가 유효한 ClickHouse SQL 문자열임을 전제로 합니다. 예를 들면:

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로 수동으로 파싱하지 않으려면 com.clickhouse.data에 위치한 헬퍼 함수 ClickHouseValues.convertToSqlExpression을 사용하세요:

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_discoverytrue로 설정하세요:

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

또는 연결 URL에서 다음과 같이 설정합니다:

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

자동 검색이 활성화된 경우 연결 URL에 모든 ClickHouse 노드를 지정할 필요가 없습니다. URL에 지정된 노드는 시드로 처리되며, Java 클라이언트는 시스템 테이블 및/또는 ClickHouse Keeper 또는 Zookeeper를 통해 추가 노드를 자동으로 검색합니다.

다음 옵션은 자동 검색 구성을 담당합니다:

속성기본값설명
auto_discoveryfalse클라이언트가 system 테이블 및/또는 Keeper/ZooKeeper에서 추가 노드를 자동으로 탐지할지 여부입니다.
node_discovery_interval0노드 탐지 간격(밀리초)입니다. 0 이하의 값으로 설정하면 한 번만 탐지합니다.
node_discovery_limit100한 번에 탐지할 수 있는 최대 노드 수입니다. 0 이하의 값으로 설정하면 제한이 없음을 의미합니다.

로드 밸런싱

Java 클라이언트는 로드 밸런싱 정책에 따라 요청을 전송할 ClickHouse 노드를 선택합니다. 일반적으로 로드 밸런싱 정책은 다음을 담당합니다:

  1. 관리되는 노드 목록에서 단일 노드를 가져옵니다.
  2. 노드의 상태 관리.
  3. 자동 검색이 활성화된 경우, 노드 검색용 백그라운드 프로세스를 필요에 따라 스케줄링하고 상태 검사를 실행합니다.

로드 밸런싱 구성 옵션 목록은 다음과 같습니다:

속성기본값설명
load_balancing_policy""로드 밸런싱 정책은 다음 중 하나일 수 있습니다:
  • firstAlive - 요청이 관리 노드 목록에서 정상 상태인 첫 번째 노드로 전송됩니다
  • random - 요청이 관리 노드 목록에서 임의의 노드로 전송됩니다
  • roundRobin - 요청이 관리 노드 목록의 각 노드로 차례대로 전송됩니다
  • ClickHouseLoadBalancingPolicy를 구현하는 완전 수식 클래스 이름(fully qualified class name) - 사용자 정의 로드 밸런싱 정책
  • 지정하지 않으면 요청은 관리 노드 목록의 첫 번째 노드로 전송됩니다
    load_balancing_tags""노드를 필터링하기 위한 로드 밸런싱 태그입니다. 요청은 지정된 태그를 가진 노드에만 전송됩니다.
    health_check_interval0헬스 체크 간격(밀리초)입니다. 0 또는 음수로 설정하면 한 번만 수행합니다.
    health_check_methodClickHouseHealthCheckMethod.SELECT_ONE헬스 체크 방식입니다. 다음 중 하나일 수 있습니다:
  • ClickHouseHealthCheckMethod.SELECT_ONE - select 1 쿼리로 확인합니다
  • ClickHouseHealthCheckMethod.PING - 프로토콜 수준의 확인 방식으로, 일반적으로 더 빠릅니다
  • node_check_interval0노드 검사 간격(밀리초)이며, 음수 값은 0으로 처리됩니다. 마지막 검사 이후 지정된 시간이 경과하면 노드 상태를 확인합니다.
    health_check_intervalnode_check_interval의 차이는, health_check_interval 옵션은 노드 목록(전체 또는 장애 노드)에 대한 상태를 확인하는 백그라운드 작업을 스케줄링하는 반면, node_check_interval은 특정 노드에 대해 마지막 검사 이후 얼마의 시간이 경과한 후 상태를 다시 확인할지를 지정한다는 점입니다
    check_all_nodesfalse모든 노드에 대해 헬스 체크를 수행할지, 비정상 노드에 대해서만 수행할지 지정합니다.

    장애 조치 및 재시도

    Java 클라이언트는 실패한 쿼리에 대한 장애 조치 및 재시도 동작을 설정할 수 있는 구성 옵션을 제공합니다:

    속성기본값설명
    failover0하나의 요청에 대해 발생할 수 있는 failover의 최대 횟수입니다. 값이 0이거나 음수이면 failover를 사용하지 않습니다. Failover는 장애가 발생한 요청을 복구하기 위해 부하 분산 정책에 따라 요청을 다른 노드로 전송합니다.
    retry0요청에 대해 재시도가 발생할 수 있는 최대 횟수입니다. 0 또는 음수 값은 재시도를 수행하지 않음을 의미합니다. 재시도는 ClickHouse 서버가 NETWORK_ERROR 오류 코드를 반환한 경우에만 동일한 노드로 요청을 다시 전송합니다
    repeat_on_session_locktrue세션이 잠겨 있는 경우 session_timeout 또는 connect_timeout 에 따라 시간 초과될 때까지 실행을 반복할지 여부입니다. ClickHouse 서버가 SESSION_IS_LOCKED 오류 코드를 반환하면 실패한 요청을 다시 시도합니다

    커스텀 HTTP 헤더 추가하기

    Java 클라이언트는 요청에 사용자 정의 HTTP 헤더를 추가해야 하는 경우 HTTP/S 전송 계층을 지원합니다. custom_http_headers 속성을 사용하십시오. 헤더는 ,로 구분되어야 하며, 헤더 키/값은 =를 사용하여 구분합니다.

    Java Client 지원

    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");