Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Java 클라이언트

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

Setup


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

초기화

Client 객체는 com.clickhouse.client.api.Client.Builder#build()를 통해 초기화됩니다. 각 클라이언트는 고유한 Context를 가지며, 클라이언트 간에 객체는 공유되지 않습니다. Builder에는 편리한 설정을 위한 구성 메서드가 있습니다.

예시:

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

ClientAutoCloseable이므로 더 이상 필요하지 않을 때 닫아야 합니다.

인증(Authentication)

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

password를 통한 인증(authentication)은 setUsername(String)setPassword(String)을 호출하여 username과 password를 설정해야 합니다:

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

액세스 토큰을 통한 인증(authentication)을 사용하려면 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")

구성

모든 설정은 인스턴스 메서드(구성 메서드라고도 함)로 정의되며, 각 값의 범위와 Context를 명확하게 나타냅니다. 주요 구성 매개변수는 하나의 범위(클라이언트 또는 작업)에서 정의되고, 서로 재정의되지 않습니다.

구성은 클라이언트 생성 시 정의됩니다. com.clickhouse.client.api.Client.Builder를 참조하십시오.

클라이언트 구성

메서드 인수 설명 기본값
addEndpoint(String endpoint) endpoint - URL 형식의 서버 주소 사용 가능한 서버 목록에 서버 엔드포인트를 추가합니다. 현재는 엔드포인트 1개만 지원합니다. none none
addEndpoint(Protocol protocol, String host, int port, boolean secure) protocol - 연결 프로토콜
host - IP 또는 호스트명
secure - HTTPS 사용 여부
사용 가능한 서버 목록에 서버 엔드포인트를 추가합니다. 현재는 엔드포인트 1개만 지원합니다. none none
enableConnectionPool(boolean enable) enable - 활성화/비활성화 플래그 연결 풀 사용 여부를 설정합니다. true connection_pool_enabled
setMaxConnections(int maxConnections) maxConnections - 연결 수 클라이언트가 각 서버 엔드포인트에 대해 열 수 있는 연결 수를 설정합니다. 10 max_open_connections
setConnectionTTL(long timeout, ChronoUnit unit) timeout - timeout 값
unit - 시간 단위
연결이 비활성 상태로 간주되기까지의 TTL을 설정합니다. -1 connection_ttl
setKeepAliveTimeout(long timeout, ChronoUnit unit) timeout - timeout 값
unit - 시간 단위
HTTP 연결 keep-alive timeout을 설정합니다. Keep-Alive를 비활성화하려면 0으로 설정합니다. - http_keep_alive_timeout
setConnectionReuseStrategy(ConnectionReuseStrategy strategy) strategy - LIFO 또는 FIFO 연결 풀이 사용할 전략을 선택합니다. FIFO connection_reuse_strategy
setDefaultDatabase(String database) database - 데이터베이스 이름 기본 데이터베이스를 설정합니다. default database

클라이언트 식별

쿼리 로그에는 요청을 발생시킨 애플리케이션을 식별하는 두 필드가 있습니다: client_namehttp_user_agent. 네이티브 TCP 프로토콜은 애플리케이션을 식별하기 위해 client_name을 사용하고, HTTP 프로토콜은 http_user_agent를 사용합니다. 클라이언트 빌더에는 두 프로토콜에 대해 올바른 값을 설정하는 setClientName 메서드가 있습니다. http_user_agent 필드는 User-Agent 헤더의 일반 형식인 application-name[/version] [(operating-system; architecture; ...)]에 따라 설정됩니다. 이 값 집합은 애플리케이션, 클라이언트 라이브러리, HTTP 클라이언트 라이브러리 등 각 계층마다 반복됩니다. setClientName 메서드로 설정한 값이 목록의 맨 앞에 옵니다.

예시:

client.setClientName("my-app-01/1.0");

다음과 같은 http_user_agent 값이 생성됩니다:

my-app-01/1.0 clickhouse-java-v2/0.9.6-SNAPSHOT (Linux; jvm:17.0.17) Apache-HttpClient/5.4.4

애플리케이션은 자신을 식별하기 위해 HTTP 헤더 User-Agent를 직접 설정할 수 있습니다. 단, clickhouse-java-v2/0.9.6-SNAPSHOT 부분이 헤더 끝에 자동으로 추가됩니다.

작업 식별

쿼리 로그에는 작업을 식별하고 쿼리 로그에 추가 정보를 남기는 데 사용할 수 있는 query_idlog_comment라는 두 개의 필드가 더 있습니다.

query_id는 작업의 고유 식별자입니다. QuerySettings 클래스의 setQueryId 메서드를 호출하면 애플리케이션에서 직접 설정할 수 있습니다.

QuerySettings querySettings = new QuerySettings();
querySettings.setQueryId("some-query-id");

log_comment은 쿼리 로그에 추가할 수 있는 주석입니다. QuerySettings 클래스의 logComment 메서드를 호출하면 애플리케이션에서 설정할 수 있습니다.

QuerySettings querySettings = new QuerySettings();
querySettings.logComment("some-comment");

서버 설정

서버 측 설정은 클라이언트 생성 시 클라이언트 수준에서 한 번 지정하거나(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 headers는 모든 작업(클라이언트 수준) 또는 개별 작업(작업 수준)에 설정할 수 있습니다.


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 - raw 데이터는 사용자가 직접 트랜스코딩해야 합니다
  • full - 클라이언트가 직접 데이터를 트랜스코딩할 수 있으며, 원시 데이터 스트림을 수신합니다
  • - - 이 포맷에서는 ClickHouse가 해당 작업을 지원하지 않습니다

이 클라이언트 버전에서 지원하는 기능은 다음과 같습니다:

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

삽입 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) 구성 옵션을 원시 포맷으로 설정합니다. 서버 설정은 아닙니다.

InsertResponse

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

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

쿼리 API

query(String sqlQuery)

sqlQuery를 그대로 전송합니다. 응답 포맷은 쿼리 설정에 따라 결정됩니다. QueryResponse는 해당 포맷을 지원하는 리더(reader)가 읽어야 하는 응답 스트림에 대한 참조를 보유합니다.

서명

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 포맷으로 데이터를 쿼리합니다. 결과를 컬렉션으로 반환합니다. 읽기 성능은 reader와 동일하지만, 전체 데이터셋을 메모리에 유지해야 하므로 더 많은 메모리가 필요합니다.

서명

List<GenericRecord> queryAll(String sqlQuery)

매개변수

sqlQuery - 서버에서 데이터를 쿼리하기 위한 SQL 표현식입니다.

반환 값

결과 데이터에 행(row) 방식으로 접근할 수 있는 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 타입을 처리할 때 사용해야 하는 시간대를 반환합니다.

예시

  • 예시 코드는 repo에서 확인할 수 있습니다
  • Spring 서비스 구현 예시 참고

공통 API

getTableSchema(String table)

table의 테이블 스키마(schema)를 가져옵니다.

서명

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

매개변수

table - 스키마 데이터를 가져올 테이블 이름입니다.

database - 대상 테이블이 정의된 데이터베이스입니다.

반환 값

테이블 컬럼 목록이 포함된 TableSchema 객체를 반환합니다.

getTableSchemaFromQuery(String sql)

SQL 구문(statement)에서 스키마(schema)를 가져옵니다.

서명

TableSchema getTableSchemaFromQuery(String sql)

매개변수

sql - 스키마를 반환할 "SELECT" SQL 문입니다.

반환 값

sql 표현식에 맞는 컬럼이 포함된 TableSchema 객체를 반환합니다.

TableSchema

register(Class<?> clazz, TableSchema schema)

schema를 사용해 데이터를 읽고 쓰는 데 사용할 Java 클래스용 직렬화(serialization) 및 역직렬화(deserialization) 레이어를 컴파일합니다. 이 메서드는 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) 애플리케이션에서 클라이언트를 사용하는 방법을 보여주는 예시입니다.

데이터 읽기

데이터를 읽는 일반적인 방법은 두 가지입니다:

  • 데이터가 포함된 InputStream을 담은 저수준 QueryResponse 객체를 반환하는 query() 메서드입니다. 일반적으로 스트리밍 읽기를 위해 ClickHouseBinaryFormatReader와 함께 사용되지만 다른 사용자 지정 리더 구현에서도 사용할 수 있습니다. QueryResponse는 결과 집합 메타데이터와 메트릭에도 접근할 수 있게 해줍니다.
  • queryAll() 메서드와 GenericRecord를 사용하면 행에 더 편리하게 접근할 수 있습니다. 이 경우 전체 결과 세트가 메모리에 로드됩니다.
  • queryRecords() 메서드는 GenericRecord 객체를 위한 반복자인 com.clickhouse.client.api.query.Records를 반환합니다. 이 메서드는 스트리밍 방식을 사용하므로 (데이터를 메모리에 로드하지 않음) GenericRecord를 통해 데이터에 접근합니다.

참고: 스트리밍 방식은 데이터를 네트워크 스트림에서 직접 읽으므로, 읽기 속도가 충분히 빠르지 않으면 서버 쓰기 timeout이 발생할 수 있습니다.

배열 읽기

ClickHouseBinaryFormatReader 메서드

  • getList(...) - 모든 Array(...)List<T>로 읽습니다. 유연한 타입 읽기에 적합한 기본값입니다. 중첩 배열도 지원합니다.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - 기본형과 호환되는 값으로 이루어진 1차원 배열에 가장 적합합니다.
  • getStringArray(...) - Array(String)용(이름으로 표현된 enum 값도 포함).
  • getObjectArray(...) - 중첩 배열을 포함한 모든 Array(...) 요소 타입에 사용할 수 있는 범용 옵션입니다. 널 허용 값을 포함하는 배열과 중첩 배열을 읽는 데 사용합니다.

모든 메서드에 대해 인덱스 기반 및 이름 기반 오버로드를 사용할 수 있습니다. 인덱스는 1부터 시작합니다. 인덱스 기반 방식은 컬럼에 직접 접근합니다. 이름 기반 메서드는 호출할 때마다 인덱스 조회가 필요합니다.

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    ClickHouseBinaryFormatReader reader = client.newBinaryFormatReader(response);
    while (reader.next() != null) {

        Object[] uint64 = reader.getObjectArray("uint64_arr"); // Array(UInt64) -> BigInteger[]
        Object[] arr2d = reader.getObjectArray("arr2d");       // Array(Array(Int64)) -> Object[]

        // nested arrays are returned as nested Object[]:
        Object[] firstInner = (Object[]) arr2d[0];
        Long firstValue = (Long) firstInner[0];
    }
}

GenericRecord 메서드

  • getList(...) - 모든 Array(...)List<T>로 읽습니다. 유연한 타입 읽기에 적합한 기본값입니다. 중첩 배열도 지원합니다.
  • getByteArray(...), getShortArray(...), getIntArray(...), getLongArray(...), getFloatArray(...), getDoubleArray(...), getBooleanArray(...) - 기본형과 호환되는 값으로 이루어진 1차원 배열에 가장 적합합니다.
  • getStringArray(...) - Array(String)용(이름으로 표현된 enum 값도 포함).
  • getObjectArray(...) - 중첩 배열을 포함한 모든 Array(...) 요소 타입에 사용할 수 있는 범용 옵션입니다. 널 허용 값을 포함하는 배열과 중첩 배열을 읽는 데 사용합니다.

모든 메서드에 대해 인덱스 기반 및 이름 기반 오버로드를 사용할 수 있습니다. 인덱스는 1부터 시작합니다. 인덱스 기반 방식은 컬럼에 직접 접근합니다. 이름 기반 메서드는 호출할 때마다 인덱스 조회가 필요합니다.

try (QueryResponse response = client.query("SELECT * FROM my_table").get()) {
    List<GenericRecord> rows = client.queryAll(
        "SELECT int_arr, arr2d_nullable FROM test_arrays ORDER BY id");

    for (GenericRecord row : rows) {
        Object[] intArr = row.getObjectArray("int_arr");                 // Array(Int32) -> Integer[]
        Object[] arr2d = row.getObjectArray("arr2d_nullable");           // Array(Array(Nullable(Int32)))

        Object[] inner = (Object[]) arr2d[0];
        Object maybeNull = inner[1]; // may be null
    }
}

마이그레이션 가이드

이전 클라이언트(V1)는 com.clickhouse.client.ClickHouseClient#builder를 시작점으로 사용했습니다. 새 클라이언트(V2)는 com.clickhouse.client.api.Client.Builder를 사용하는 유사한 패턴을 따릅니다. 주요 차이점은 다음과 같습니다:

  • 구현을 가져오기 위해 service loader를 사용하지 않습니다. com.clickhouse.client.api.Client는 향후 다양한 구현을 위한 파사드 클래스입니다.
  • 구성 소스 수가 더 적습니다. 하나는 빌더에 제공되고, 다른 하나는 작업 설정(QuerySettings, InsertSettings)에 있습니다. 이전 버전에서는 노드별 구성을 사용했고, 경우에 따라 환경 변수도 불러왔습니다.

구성 매개변수 일치

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

  • 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#HOST Client.Builder#addEndpoint
ClickHouseDefaults#PROTOCOL V2는 HTTP만 지원합니다
ClickHouseDefaults#DATABASE
ClickHouseClientOption#DATABASE
Client.Builder#setDefaultDatabase
ClickHouseDefaults#USER Client.Builder#setUsername
ClickHouseDefaults#PASSWORD Client.Builder#setPassword
ClickHouseClientOption#CONNECTION_TIMEOUT Client.Builder#setConnectTimeout
ClickHouseClientOption#CONNECTION_TTL Client.Builder#setConnectionTTL
ClickHouseHttpOption#MAX_OPEN_CONNECTIONS Client.Builder#setMaxConnections
ClickHouseHttpOption#KEEP_ALIVE
ClickHouseHttpOption#KEEP_ALIVE_TIMEOUT
Client.Builder#setKeepAliveTimeout
ClickHouseHttpOption#CONNECTION_REUSE_STRATEGY Client.Builder#setConnectionReuseStrategy
ClickHouseHttpOption#USE_BASIC_AUTHENTICATION Client.Builder#useHTTPBasicAuth

일반적인 차이점

  • Client V2는 이식성을 높이기 위해 전용 클래스 사용을 줄였습니다. 예를 들어 V2는 서버에 데이터를 쓰는 데 java.io.InputStream의 모든 구현을 사용할 수 있습니다.
  • Client V2에서 async 설정의 기본값은 off입니다. 즉, 추가 스레드가 생성되지 않으며 클라이언트 제어를 애플리케이션에서 더 많이 할 수 있습니다. 이 설정은 대부분의 사용 사례에서 off로 유지해야 합니다. async를 활성화하면 요청마다 별도의 스레드가 생성됩니다. 이는 애플리케이션에서 제어하는 실행기를 사용할 때만 의미가 있습니다(참조: 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) 메서드를 사용해 레코드 컬렉션으로 읽을 수 있습니다. 이 메서드는 데이터를 메모리에 읽어 들인 뒤 connection을 해제합니다. 별도의 추가 처리는 필요하지 않습니다. 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");
}

DB 서버의 프로토콜을 통해 통신하기 위한 Java 클라이언트 라이브러리입니다. 현재 구현은 HTTP 인터페이스만 지원합니다. 이 라이브러리는 서버에 요청을 보내기 위한 자체 API를 제공합니다.

Setup

<!-- 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 버전부터 driver는 새 클라이언트 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();
            // 타입 변환
            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`는 RowBinary 포맷의 데이터 소스입니다
        .executeAndWait()) {
            ClickHouseResponseSummary summary = response.getSummary();
            summary.getWrittenRows();
}

저장소에서 전체 코드 예시를 확인하십시오.

RowBinary 인코딩

RowBinary 포맷에 대한 자세한 내용은 해당 페이지를 참조하십시오.

코드 예시가 있습니다.

기능

압축(Compression)

클라이언트는 기본적으로 LZ4 압축(Compression)을 사용하며, 이를 위해 다음 의존성이 필요합니다:

<!-- 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. connection 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();

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

다중 쿼리

동일한 세션 내에서 워커 스레드로 여러 쿼리를 순차적으로 실행합니다:

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()) {
            //...
        }
}

노드 디스커버리

Java 클라이언트는 ClickHouse 노드를 자동으로 검색하는 디스커버리 기능을 제공합니다. 자동 디스커버리는 기본적으로 비활성화되어 있습니다. 활성화하려면 auto_discoverytrue로 설정하십시오:

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

또는 연결 URL에서:

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

자동 디스커버리가 활성화된 경우, 연결 URL에 모든 ClickHouse 노드를 지정하지 않아도 됩니다. URL에 지정된 노드는 시드(seed)로 처리되며, Java 클라이언트가 시스템 테이블(system tables) 및/또는 clickhouse-keeper나 ZooKeeper를 통해 추가 노드를 자동으로 검색합니다.

다음 옵션들은 자동 디스커버리 구성을 담당합니다:

속성 기본값 설명
auto_discovery false 클라이언트가 시스템 테이블(system tables) 및/또는 clickhouse-keeper/ZooKeeper에서 추가 노드를 자동으로 찾을지 여부입니다.
node_discovery_interval 0 밀리초 단위의 노드 디스커버리 간격이며, 값이 0 이하이면 1회성 디스커버리를 의미합니다.
node_discovery_limit 100 한 번에 디스커버리할 수 있는 최대 노드 수입니다. 값이 0 이하이면 제한이 없습니다.

로드 밸런싱

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

  1. 관리형 노드 목록에서 노드를 조회합니다.
  2. 노드 상태 관리.
  3. 선택적으로 노드 디스커버리용 백그라운드 프로세스를 예약하고(자동 디스커버리가 활성화된 경우) 상태 점검을 실행합니다.

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

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

    장애 조치 및 재시도

    Java 클라이언트는 실패한 쿼리에 대한 failover 및 retry 동작을 구성할 수 있는 옵션을 제공합니다:

    속성 기본값 설명
    장애 조치 0 요청에서 장애 조치가 발생할 수 있는 최대 횟수입니다. 0 또는 음수 값은 장애 조치를 수행하지 않음을 의미합니다. 장애 조치는 실패한 요청을 다른 노드로 전송하여(로드 밸런싱 정책에 따름) 장애를 복구합니다.
    재시도 0 요청에 대해 재시도할 수 있는 최대 횟수입니다. 0 또는 음수 값은 재시도하지 않음을 의미합니다. 재시도는 요청을 동일한 노드로 다시 보내며, ClickHouse 서버가 NETWORK_ERROR 오류 코드를 반환한 경우에만 수행됩니다.
    repeat_on_session_lock true 세션이 잠긴 경우 시간 초과(session_timeout 또는 connect_timeout 기준)될 때까지 실행을 반복할지 여부입니다. ClickHouse 서버가 SESSION_IS_LOCKED 오류 코드를 반환하면 실패한 요청을 다시 시도합니다

    사용자 지정 HTTP 헤더 추가하기

    요청에 사용자 지정 HTTP 헤더를 추가해야 하는 경우 Java 클라이언트는 HTTP/S 전송 계층을 지원합니다. custom&#95;http&#95;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");
    Navigation