-
ClickHouseClient(권장): 싱글턴으로 사용하도록 설계된 고수준의 스레드 안전한 클라이언트입니다. 쿼리와 대량 삽입을 위한 간단한 비동기 API를 제공합니다. 대부분의 애플리케이션에 가장 적합합니다. -
ADO.NET (
ClickHouseDataSource,ClickHouseConnection,ClickHouseCommand): 표준 .NET 데이터베이스 추상화입니다. ORM 통합(Dapper, Linq2db)과 ADO.NET 호환성이 필요할 때 필수입니다.ClickHouseBulkCopy는 ADO.NET 연결을 사용해 데이터를 효율적으로 삽입할 수 있도록 돕는 헬퍼 클래스입니다.ClickHouseBulkCopy는 더 이상 권장되지 않으며 향후 릴리스에서 제거될 예정이므로, 대신ClickHouseClient.InsertBinaryAsync를 사용하십시오.
마이그레이션 가이드
.csproj파일에서 패키지 이름을ClickHouse.Driver로 변경하고, NuGet의 최신 버전으로 업데이트합니다.- 코드베이스의 모든
ClickHouse.Client참조를ClickHouse.Driver로 변경합니다.
지원되는 .NET 버전
ClickHouse.Driver는 다음과 같은 .NET 버전을 지원합니다.
- .NET 6.0
- .NET 8.0
- .NET 9.0
- .NET 10.0
설치
빠른 시작
구성
- 연결 문자열: 호스트, 인증 자격 증명, 기타 연결 옵션을 지정하는 세미콜론으로 구분된 키/값 쌍입니다.
ClickHouseClientSettingsobject: 설정 파일에서 로드하거나 코드에서 설정할 수 있는 강타입 구성 객체입니다.
연결 설정
데이터 포맷 및 직렬화
세션 관리
UseSession 플래그를 사용하면 서버 세션이 유지되어 SET SQL 문과 임시 테이블을 사용할 수 있습니다. 세션은 60초 동안 비활성 상태가 지속되면(기본 timeout) 재설정됩니다. 세션 수명은 ClickHouse SQL 문 또는 서버 구성을 통해 세션 설정을 지정하여 연장할 수 있습니다.ClickHouseConnection 클래스는 일반적으로 병렬 작업을 허용합니다(여러 스레드가 동시에 쿼리를 실행할 수 있음). 하지만 UseSession 플래그를 활성화하면 어떤 시점에도 connection당 활성 쿼리는 하나만 허용됩니다(이는 서버 측 제한입니다).보안
HTTP 클라이언트 구성
로깅 및 디버깅
사용자 지정 설정 & 역할
연결 문자열을 사용해 사용자 지정 설정을 지정할 때는
set_ 접두사를 사용하십시오. 예: “set_max_threads=4”. ClickHouseClientSettings 객체를 사용할 때는 set_ 접두사를 사용하지 마십시오.사용 가능한 설정의 전체 목록은 여기를 참조하십시오.연결 문자열 예시
기본 연결
사용자 지정 ClickHouse 설정 사용 시
QueryOptions
QueryOptions를 사용하면 쿼리마다 클라이언트 수준 설정을 재정의할 수 있습니다. 모든 속성은 선택 사항이며, 지정한 경우에만 클라이언트 기본값을 덮어씁니다.
예시:
InsertOptions
InsertOptions는 InsertBinaryAsync를 통한 대량 삽입 작업에 필요한 설정을 QueryOptions에 추가한 옵션입니다.
모든
QueryOptions 속성은 InsertOptions에서도 사용할 수 있습니다.
예시:
스키마 확인 쿼리 건너뛰기
InsertBinaryAsync는 각 삽입 전에 컬럼 타입을 확인하기 위해 SELECT ... WHERE 1=0 쿼리를 전송합니다. 높은 처리량이 필요한 환경에서는 두 가지 방법으로 이 오버헤드를 제거할 수 있습니다:
옵션 1: 컬럼 타입을 명시적으로 제공
컴파일 시점에 테이블 스키마를 알고 있다면 ColumnTypes를 통해 직접 전달하십시오. 그러면 스키마 확인 쿼리는 전혀 전송되지 않습니다:
UseSchemaCache = true로 설정하면 스키마를 한 번만 쿼리한 뒤 동일한 ClickHouseClient 인스턴스의 후속 삽입에서 이를 재사용합니다:
ColumnTypes는UseSchemaCache보다 우선합니다. 둘 다 설정된 경우 명시적으로 지정한 타입이 사용됩니다.- 스키마 캐시는
ALTER TABLE변경 사항을 감지하지 않습니다. 테이블 스키마를 수정한 경우 새ClickHouseClient를 생성하거나 해당 테이블에서는UseSchemaCache를 사용하지 마십시오. - 캐시 범위는
ClickHouseClient인스턴스로 한정되며, 키는 (데이터베이스, 테이블)입니다. 동일한 테이블의 서로 다른 컬럼 부분 집합은 하나의 캐시된 스키마를 공유합니다.
ClickHouseClient
ClickHouseClient는 ClickHouse와 상호 작용할 때 권장되는 API입니다. 스레드 안전을 보장하며, singleton으로 사용하도록 설계되었고, 내부적으로 HTTP 연결 풀링을 관리합니다.
클라이언트 생성
ClickHouseClientSettings 객체를 사용해 ClickHouseClient를 생성합니다. 사용 가능한 옵션은 구성 섹션을 참조하십시오.
ClickHouse Cloud 서비스의 세부 정보는 ClickHouse Cloud 콘솔에서 확인할 수 있습니다.
서비스를 선택하고 Connect를 클릭합니다:
**C#**을 선택합니다. 아래에 연결 세부 정보가 표시됩니다.
자가 관리형 ClickHouse를 사용하는 경우 연결 세부 정보는 ClickHouse 관리자에 의해 설정됩니다.
연결 문자열 사용:
ClickHouseClientSettings를 사용할 수 있습니다:
IHttpClientFactory를 사용하세요:
ClickHouseClient는 애플리케이션 전반에서 장기간 유지하며 공유할 수 있도록 설계되었습니다. 한 번만 생성하고(일반적으로 싱글턴으로) 모든 데이터베이스 작업에 재사용하세요. 클라이언트는 내부적으로 HTTP 연결 풀링을 관리합니다.쿼리 실행
ExecuteNonQueryAsync를 사용하십시오:
ExecuteScalarAsync를 사용합니다:
데이터 삽입
매개변수화된 삽입
ExecuteNonQueryAsync를 사용해 매개변수화된 쿼리로 데이터를 삽입합니다. 매개변수 타입은 SQL에서 {name:Type} 구문으로 지정해야 합니다:
대량 삽입
InsertBinaryAsync를 사용합니다. 이 메서드는 ClickHouse의 네이티브 행 바이너리 형식으로 데이터를 스트리밍하고, 병렬 Batch 업로드를 지원하며, 매개변수화 쿼리에서 발생할 수 있는 “URL too long” 오류를 방지합니다.
InsertOptions를 사용해 배칭과 병렬성을 설정합니다:
- 클라이언트는 삽입 전에
SELECT * FROM <table> WHERE 1=0을 통해 테이블 구조를 자동으로 가져옵니다. 제공된 값은 대상 컬럼 타입과 일치해야 합니다. 이 쿼리를 건너뛰려면InsertOptions.ColumnTypes또는InsertOptions.UseSchemaCache를 사용하세요. MaxDegreeOfParallelism > 1이면 배치가 병렬로 업로드됩니다. 세션은 병렬 삽입과 호환되지 않으므로 세션을 비활성화하거나MaxDegreeOfParallelism = 1로 설정하세요.- 제공되지 않은 컬럼에 서버가 DEFAULT 값을 적용하도록 하려면
InsertOptions.Format에서RowBinaryFormat.RowBinaryWithDefaults를 사용하세요.
POCO 삽입
object[] 배열을 구성하는 대신, 강력한 형식이 지정된 POCO 객체를 직접 삽입할 수 있습니다. 타입을 한 번 등록한 다음 IEnumerable<T>를 전달하면 됩니다:
매핑된 모든 속성에 명시적인
Type이 지정되면 스키마 확인 쿼리는 완전히 생략됩니다. 일부 속성에만 명시적 타입이 있으면 드라이버는 전체 컬럼 집합에 대해 스키마 확인 쿼리를 사용합니다.
InsertBinaryAsync<T>는 object[] 오버로드와 동일한 InsertOptions(배칭, 병렬 처리, 스키마 캐싱)를 지원합니다.
object[] 오버로드와 달리 InsertBinaryAsync<T>는 명시적인 컬럼 목록을 받지 않습니다. 컬럼은 등록된 타입에 매핑된 속성을 기준으로 결정됩니다. 삽입할 컬럼을 제어하려면 [ClickHouseNotMapped]를 사용해 속성을 제외하거나 [ClickHouseColumn(Name = "...")]를 사용해 이름을 변경하십시오.InsertOptions에서 ColumnTypes를 설정하면 POCO 특성보다 우선 적용됩니다.스키마 진화
DEFAULT(또는 다른 기본 표현식)가 있는 새 컬럼은 서버가 자동으로 채웁니다. 코드를 변경하거나 다시 등록할 필요가 없습니다.
데이터 읽기
ExecuteReaderAsync를 사용합니다. 반환된 ClickHouseDataReader는 GetInt64(), GetString(), GetFieldValue<T>() 같은 메서드를 통해 결과 컬럼에 형식에 맞게 접근할 수 있도록 해줍니다.
다음 행으로 이동하려면 Read()를 호출합니다. 더 이상 행이 없으면 false를 반환합니다. 컬럼은 인덱스(0부터 시작) 또는 컬럼 이름으로 접근할 수 있습니다.
SQL 매개변수
{parameter_name:DataType} 포맷을 사용합니다.
예시:
SQL ‘bind’ 매개변수는 HTTP URI 쿼리 매개변수로 전달되므로, 너무 많이 사용하면 “URL이 너무 깁니다” 예외가 발생할 수 있습니다. 이 제한을 피하려면 대량 데이터 삽입에는
InsertBinaryAsync를 사용하세요.쿼리 ID
query_id가 할당되며, 이 값은 system.query_log 테이블에서 데이터를 조회하거나 장시간 실행 중인 쿼리를 취소하는 데 사용할 수 있습니다. QueryOptions를 통해 사용자 지정 쿼리 ID를 지정할 수 있습니다:
사용자 지정 매개변수 유형 매핑
@ 스타일 매개변수(예: WHERE id = @id)를 사용하면 드라이버가 .NET 값 형식을 기준으로 ClickHouse 유형을 자동으로 추론합니다. 예를 들어 int는 Int32로, DateTime은 DateTime으로 매핑됩니다.
이 기본 동작을 재정의하려면 ClickHouseClientSettings에서 ParameterTypeResolver를 설정하십시오. 이렇게 하면 각 개별 매개변수에 ClickHouseType을 일일이 설정하지 않고도, 모든 DateTime 매개변수에 밀리초 정밀도를 위해 DateTime64(3)를 사용하거나 모든 decimal에 특정 scale을 적용할 수 있어 유용합니다.
간단한 유형 매핑에 DictionaryParameterTypeResolver 사용:
IParameterTypeResolver:
값을 고려하거나 이름을 기준으로 확인해야 하는 경우 IParameterTypeResolver 인터페이스를 직접 구현하십시오. 기본 추론을 사용하도록 하려면 null을 반환하십시오:
QueryOptions.ParameterTypeResolver를 통해 리졸버를 설정할 수 있습니다. 설정된 경우 클라이언트 수준의 리졸버보다 우선합니다.
타입 결정 우선순위:
리졸버는 우선순위 체인의 한 단계입니다. 우선순위가 높은 순서부터 낮은 순서는 다음과 같습니다.
- 매개변수에 명시적으로 설정된
ClickHouseType - 쿼리의
{name:Type}구문에 지정된 SQL type hint IParameterTypeResolver(QueryOptions.ParameterTypeResolver에서 가져오고, 없으면ClickHouseClientSettings.ParameterTypeResolver를 사용)- 기본 제공 타입 추론(
TypeConverter.ToClickHouseType)
ClickHouseConnection 경로에서도 작동합니다. 설정은 클라이언트에서 생성된 연결에 상속됩니다.
Raw 스트리밍
ExecuteRawResultAsync를 사용합니다. 이는 데이터를 파일로 내보내거나 다른 시스템으로 그대로 전달할 때 유용합니다:
JSONEachRow, CSV, TSV, Parquet, Native. 모든 옵션은 포맷 문서를 참조하십시오.
Raw 스트림 삽입
InsertRawStreamAsync를 사용하면 CSV, JSON, Parquet 또는 기타 지원되는 ClickHouse 포맷의 파일 또는 메모리 스트림에서 데이터를 직접 삽입할 수 있습니다.
CSV 파일에서 삽입:
데이터 수집 동작을 제어하는 옵션은 포맷 설정 문서에서 확인하십시오.
추가 예시
ADO.NET
ClickHouseConnection, ClickHouseCommand, ClickHouseDataReader를 통해 ADO.NET을 완전하게 지원합니다. 이 API는 ORM 통합(Dapper, Linq2db)과 표준 .NET 데이터베이스 추상화가 필요할 때 사용해야 합니다.
ClickHouseDataSource를 사용한 수명 주기 관리
ClickHouseDataSource에서 연결을 생성하세요. DataSource는 내부적으로 단일 ClickHouseClient를 관리하며, 모든 연결은 해당 HTTP 연결 풀을 공유합니다.
ClickHouseCommand 사용하기
ExecuteNonQueryAsync()- INSERT, UPDATE, DELETE, DDL 문에 사용됩니다ExecuteScalarAsync()- 첫 번째 행의 첫 번째 컬럼을 반환합니다ExecuteReaderAsync()- 결과를 순회할 수 있는ClickHouseDataReader를 반환합니다
ClickHouseDataReader 사용
ClickHouseDataReader는 쿼리 결과에 타입이 지정된 형태로 접근할 수 있도록 제공합니다:
모범 사례
연결 수명 및 풀링
ClickHouse.Driver는 내부적으로 System.Net.Http.HttpClient를 사용합니다. HttpClient에는 엔드포인트별 연결 풀이 있습니다. 그에 따라 다음과 같은 특성이 있습니다.
- 데이터베이스 세션은 연결 풀에서 관리하는 HTTP 연결을 통해 다중화됩니다.
- HTTP 연결은 풀에서 자동으로 재사용됩니다.
ClickHouseClient또는ClickHouseConnection객체를 dispose한 뒤에도 연결이 유지될 수 있습니다.
DateTime 처리
-
가능하면 항상 UTC를 사용하십시오. 타임스탬프는
DateTime('UTC')컬럼에 저장하고, 코드에서는DateTimeKind.Utc를 사용하십시오. 이렇게 하면 시간대와 관련된 모호성을 없앨 수 있습니다. -
시간대를 명시적으로 처리해야 할 때는
DateTimeOffset을 사용하십시오.DateTimeOffset은 항상 특정 시점을 나타내며, 오프셋 정보도 함께 포함합니다. -
SQL type hint에 시간대를 지정하십시오. UTC가 아닌 컬럼을 대상으로 하는
UnspecifiedDateTime 값을 매개변수로 사용할 때는 SQL에 시간대를 포함하십시오.
비동기 삽입
CustomSettings 또는 연결 문자열(connection string)을 통해 비동기 삽입을 활성화하세요:
wait_for_async_insert로 제어):
주요 설정:
세션
- 임시 테이블 (
CREATE TEMPORARY TABLE) - 여러 SQL 문에 걸쳐 쿼리 컨텍스트 유지
- 세션 수준 설정 (
SET max_threads = 4)
지원 데이터 타입
ClickHouse.Driver는 모든 ClickHouse 데이터 타입을 지원합니다. 아래 표는 데이터베이스에서 데이터를 읽을 때 ClickHouse 타입과 네이티브 .NET 타입 간의 매핑을 보여줍니다.
타입 매핑: ClickHouse에서 읽어올 때
정수 타입
부동 소수점 타입
Decimal 타입
Decimal 타입 변환은 UseCustomDecimals 설정으로 제어됩니다.
불리언 타입
String 타입
기본적으로
String 및 FixedString(N) 컬럼은 모두 string으로 반환됩니다. 이를 byte[]로 읽으려면 연결 문자열에서 ReadStringsAsByteArrays=true를 설정하세요. 이 옵션은 유효한 UTF-8이 아닐 수 있는 바이너리 데이터를 저장할 때 유용합니다.날짜 및 시간 타입
ClickHouse는
DateTime 및 DateTime64 값을 내부적으로 Unix timestamp(epoch 이후의 초 또는 그보다 작은 단위)로 저장합니다. 저장은 항상 UTC로 이루어지지만, 컬럼에는 연결된 시간대가 있을 수 있으며 이 시간대는 값이 표시되고 해석되는 방식에 영향을 줍니다.
DateTime 값을 읽을 때 DateTime.Kind 속성은 컬럼의 시간대에 따라 설정됩니다:
UTC가 아닌 컬럼의 경우, 반환되는
DateTime은 해당 시간대의 현지 시각을 나타냅니다. 해당 시간대에 맞는 올바른 오프셋이 포함된 DateTimeOffset을 가져오려면 ClickHouseDataReader.GetDateTimeOffset()을 사용하십시오:
DateTime('Europe/Amsterdam')이 아니라 DateTime)의 경우, 드라이버는 Kind=Unspecified인 DateTime을 반환합니다. 이렇게 하면 시간대에 대해 별도로 가정하지 않고, 저장된 wall-clock time을 정확히 그대로 유지할 수 있습니다.
명시적인 시간대가 없는 컬럼에서 시간대 인식 동작이 필요하다면, 다음 중 하나를 사용하십시오.
- 컬럼 정의에 명시적인 시간대를 사용합니다:
DateTime('UTC')또는DateTime('Europe/Amsterdam') - 읽은 후 시간대를 직접 적용합니다.
JSON 타입
JSON 컬럼의 반환 타입은
JsonReadMode 설정으로 제어됩니다:
-
Binary(기본값):System.Text.Json.Nodes.JsonObject를 반환합니다. JSON 데이터에 구조적으로 접근할 수 있지만, IP 주소, UUID, 큰 Decimal 값과 같은 ClickHouse의 특수 타입은 JSON 구조 안에서 문자열 표현으로 변환됩니다. -
String: 원본 JSON을string으로 반환합니다. ClickHouse의 JSON 표현을 그대로 유지하므로, parsing 없이 JSON을 그대로 전달해야 하거나 역직렬화를 직접 처리하려는 경우에 유용합니다.
기타 타입
Dynamic 및 Variant 타입은 각 행의 실제 기반 타입에 해당하는 타입으로 변환됩니다.
Geometry 타입
Geometry 타입은 모든 Geometry 타입을 저장할 수 있는 Variant 타입입니다. 대응되는 타입으로 변환됩니다.
타입 매핑: ClickHouse에 쓰기
정수 타입
부동 소수점 타입
불리언 타입
String 타입
날짜 및 시간 타입
드라이버는 값을 쓸 때
DateTime.Kind를 따릅니다:
DateTimeOffset 값은 항상 정확한 시점을 유지합니다.
예시: UTC DateTime (시점 유지)
DateTimeKind.Utc 또는 DateTimeOffset을 사용하십시오. 이렇게 하면 서버 시간대, 클라이언트 시간대, 컬럼 시간대와 관계없이 코드가 항상 일관되게 동작합니다.
HTTP 매개변수와 대량 복사
Unspecified DateTime 값을 쓸 때는 HTTP 매개변수 바인딩과 대량 복사 방식 사이에 중요한 차이가 있습니다:
대량 복사는 대상 컬럼의 시간대를 알고 있으므로 Unspecified 값을 해당 시간대로 올바르게 해석합니다.
HTTP 매개변수는 컬럼의 시간대를 자동으로 알지 못합니다. 따라서 SQL 타입 힌트에 시간대를 지정해야 합니다:
Decimal 타입
JSON 타입
JSON을 쓸 때의 동작은
JsonWriteMode 설정으로 제어됩니다:
-
String(기본값):string,JsonObject,JsonNode또는 임의의 객체를 허용합니다. 모든 입력은System.Text.Json.JsonSerializer를 통해 직렬화되며, 서버 측에서 파싱할 수 있도록 JSON 문자열로 전송됩니다. 가장 유연한 모드이며 타입 등록 없이도 사용할 수 있습니다. -
Binary: 등록된 POCO 타입만 허용합니다. 데이터는 클라이언트 측에서 전체 타입 힌트 지원과 함께 ClickHouse의 바이너리 JSON 포맷으로 변환됩니다. 사용 전에connection.RegisterJsonSerializationType<T>()를 호출해야 합니다. 이 모드에서string또는JsonNode값을 쓰면ArgumentException이 발생합니다.
타입이 지정된 JSON 컬럼
JSON(id UInt64, price Decimal128(2))), 드라이버는 이 힌트를 사용해 값을 원래 타입 정보를 온전히 유지하면서 직렬화합니다. 이를 통해 일반 JSON으로 직렬화할 때 정밀도가 손실될 수 있는 UInt64, Decimal, UUID, DateTime64 같은 타입의 정밀도를 보존할 수 있습니다.
POCO 직렬화
JsonWriteMode에 따라 두 가지 방식으로 JSON 컬럼에 쓸 수 있습니다:
String 모드(기본값): POCO는 System.Text.Json.JsonSerializer를 통해 직렬화됩니다. 타입 등록은 필요하지 않습니다. 가장 간단한 방식이며 익명 객체에도 사용할 수 있습니다.
Binary 모드: POCO는 드라이버의 바이너리 JSON 포맷을 사용해 직렬화되며, 타입 힌트를 완전히 지원합니다. 사용 전에 connection.RegisterJsonSerializationType<T>()로 타입을 등록해야 합니다. 이 모드에서는 특성을 사용해 사용자 지정 경로 매핑을 적용할 수 있습니다:
-
[ClickHouseJsonPath("path")]: 속성을 사용자 지정 JSON 경로에 매핑합니다. 중첩 구조를 다루거나 속성 이름이 원하는 JSON 키와 다를 때 유용합니다. Binary 모드에서만 작동합니다. -
[ClickHouseJsonIgnore]: 직렬화 대상에서 속성을 제외합니다. Binary 모드에서만 작동합니다.
UserId는 userid가 아니라 UserId로 정의된 힌트에만 일치합니다. 이는 userName과 UserName 같은 경로가 각각 별도의 필드로 공존할 수 있도록 허용하는 ClickHouse 동작과 일치합니다.
제한 사항(Binary 모드 전용):
- POCO 타입은 직렬화 전에
connection.RegisterJsonSerializationType<T>()를 사용해 connection에 등록해야 합니다. 등록되지 않은 타입을 직렬화하려고 하면ClickHouseJsonSerializationException이 발생합니다. - 딕셔너리 및 배열/리스트 속성이 올바르게 직렬화되려면 컬럼 정의에 타입 힌트가 필요합니다. 힌트가 없으면 대신 String 모드를 사용하십시오.
- POCO 속성의 NULL 값은 컬럼 정의의 해당 경로에
Nullable(T)타입 힌트가 있을 때만 기록됩니다. ClickHouse는 동적 JSON 경로 내부에Nullable타입을 허용하지 않으므로, 힌트가 없는 null 속성은 건너뜁니다. ClickHouseJsonPath및ClickHouseJsonIgnore특성은 String 모드에서는 무시됩니다(Binary 모드에서만 작동합니다).
기타 타입
Geometry 타입
쓰기에서 지원되지 않음
중첩 타입 처리
Nested(...))은 배열 문법으로 읽고 쓸 수 있습니다.
로깅 및 진단
Microsoft.Extensions.Logging 추상화와 통합되어 가볍고 필요할 때 선택적으로 사용할 수 있는 로깅을 제공합니다. 활성화하면 드라이버는 connection 수명 주기 이벤트, 명령 실행, 전송 작업, 대량 삽입 작업에 대해 구조화된 메시지를 출력합니다. 로깅은 완전히 선택 사항이므로 로거를 구성하지 않은 애플리케이션도 추가 오버헤드 없이 계속 실행됩니다.
빠른 시작
appsettings.json 사용
인메모리 구성 사용하기
범주 및 이미터
예시: 연결 문제 진단하기
- HTTP 클라이언트 팩터리 선택(기본 풀 또는 단일 연결)
- HTTP handler 구성(SocketsHttpHandler 또는 HttpClientHandler)
- 연결 풀 설정(MaxConnectionsPerServer, PooledConnectionLifetime 등)
- timeout 설정(ConnectTimeout, Expect100ContinueTimeout 등)
- SSL/TLS 구성
- 연결 열림/닫힘 이벤트
- 세션 ID 추적
디버그 모드: 네트워크 추적 및 진단
ClickHouse.Driver.Diagnostic.TraceHelper 클래스를 통해 수동으로 활성화할 수 있습니다). 이벤트는 ClickHouse.Driver.NetTrace 범주에 기록됩니다. 경고: 이렇게 하면 매우 상세한 로그가 대량으로 생성되며 성능에도 영향을 줍니다. 프로덕션 환경에서는 디버그 모드를 활성화하지 않는 것이 좋습니다.
OpenTelemetry
System.Diagnostics.Activity API를 통해 OpenTelemetry 분산 트레이싱을 기본으로 지원합니다. 이 기능을 활성화하면 드라이버가 데이터베이스 작업에 대한 스팬을 생성하며, 생성된 스팬은 Jaeger나 ClickHouse 자체(OpenTelemetry Collector 사용)와 같은 관측성 백엔드로 내보낼 수 있습니다.
트레이싱 활성화
ActivitySource를 OpenTelemetry 구성에 추가하세요:
스팬 속성
구성 옵션
ClickHouseDiagnosticsOptions를 사용해 추적 동작을 제어합니다:
TLS 구성
사용자 지정 인증서 유효성 검사
ServerCertificateCustomValidationCallback 핸들러가 구성된 자체 HttpClient를 제공하십시오:
사용자 지정 HttpClient를 제공할 때의 중요 사항
- 자동 압축 해제: 압축을 비활성화하지 않았다면
AutomaticDecompression을 활성화해야 합니다(압축은 기본적으로 활성화되어 있습니다). - 유휴 시간 제한: 반쯤 열린 연결로 인한 연결 오류를 방지하려면
PooledConnectionIdleTimeout을 서버의keep_alive_timeout보다 작게 설정하십시오(ClickHouse Cloud에서는 10초).
ORM 지원
ClickHouseConnection)를 사용해야 합니다. 연결 수명 주기를 올바르게 관리하려면 ClickHouseDataSource에서 연결을 생성하십시오:
Dapper
ClickHouse.Driver는 Dapper와 함께 사용할 수 있습니다. 드라이버는 Dapper의 @parameter 구문을 ClickHouse의 네이티브 {parameter:Type} 구문으로 자동 변환하며, 타입은 .NET 값에서 자동으로 추론됩니다.
적절한 연결 수명 주기 관리를 위해 ClickHouseDataSource를 사용하세요:
매개변수 전달 방식
DynamicParameters (딕셔너리나 익명 객체에서):
POCO로 쿼리 결과 매핑하기
ClickHouse 네이티브 매개변수 구문
{param:Type} 구문을 직접 사용하고, 매개변수 값에는 Dictionary<string, object>를 사용하십시오. 동일한 매개변수에 @param 구문과 {param:Type} 구문을 함께 사용하지 마십시오.
WHERE IN
WHERE id IN (@Ids1, @Ids2, @Ids3)로 재작성하고, 드라이버는 확장된 각 매개변수를 개별적으로 변환합니다.
배열 매개변수를 사용하는 ClickHouse의 has() 함수도 동작합니다:
사용자 지정 타입 핸들러
ITuple, BigInteger, ClickHouseDecimal)은 애플리케이션 시작 시 핸들러를 등록해야 합니다:
Dapper.Contrib
GetAll<T>() 및 Get<T>(id)는 작동합니다. Insert<T>()는 작동하지 않습니다. SQL Server 구문(SCOPE_IDENTITY, [])을 생성하기 때문입니다. 대신 ClickHouseClient 네이티브 InsertBinaryAsync 메서드를 사용하는 것이 좋습니다.
제한 사항
Linq2db
DataConnection을 생성합니다:
BulkCopyAsync를 사용하세요.
Entity Framework Core
SaveChanges를 통해 데이터를 삽입하는 작업을 모두 익숙한 EF Core 패턴으로 수행할 수 있습니다.
- NuGet:
ClickHouse.EntityFrameworkCore - 소스: GitHub
이 프로바이더는 현재 활발히 개발되고 있습니다. 현재 릴리스에서는 LINQ 쿼리(JOIN, 서브쿼리, 집합 연산 포함),
SaveChanges / BulkInsertAsync를 통한 INSERT, 전체 DDL(CREATE / ALTER / DROP)을 포함한 마이그레이션, 그리고 ClickHouse 전용 테이블 엔진 구성을 지원합니다. UPDATE / DELETE는 지원되지 않습니다.설치
빠른 시작
DbContext를 정의한 다음 LINQ로 쿼리합니다:
지원되는 타입
Decimal128/Decimal256 컬럼의 전체 정밀도가 필요한 경우 decimal 대신 ClickHouse.Driver.Numerics의 ClickHouseDecimal을 사용하세요 — .NET decimal은 유효 자릿수가 28~29자리로 제한됩니다.
지원되는 LINQ 작업
Where, OrderBy, Take, Skip, Select, First, Single, Any, All, Count, Distinct, AsNoTracking
GROUP BY 및 집계: Count, LongCount, Sum, Average, Min, Max와 함께 사용하는 GroupBy — HAVING(.GroupBy() 뒤의 .Where()), 단일 프로젝션의 여러 집계, 집계 결과에 대한 OrderBy를 포함합니다.
조인: Join(INNER), GroupJoin/SelectMany 패턴(LEFT 및 CROSS). LEFT JOIN은 일치하는 항목이 없는 행에 실제 null을 반환합니다(아래의 LEFT JOIN NULL 의미 체계 참조).
서브쿼리: 상관 Contains / IN, Any / EXISTS, All, 그리고 프로젝션 내 스칼라 서브쿼리.
집합 연산: Concat(→ UNION ALL), Union(→ UNION DISTINCT), Intersect, Except.
인라인 로컬 컬렉션: 메모리 내 컬렉션(int[], List<T> 등)에 대한 조인과 Contains는 일련의 UNION으로 변환됩니다.
문자열 메서드: Contains, StartsWith, EndsWith, IndexOf, Replace, Substring, Trim/TrimStart/TrimEnd, ToLower, ToUpper, Length, IsNullOrEmpty, Concat(및 + 연산자).
수학 함수: 표준 Math 및 MathF 메서드는 산술, 로그, 삼각, 유틸리티 함수를 비롯해 해당 ClickHouse 함수로 변환됩니다.
LEFT JOIN NULL 의미 체계
set_join_use_nulls=1을 자동으로 주입합니다.
ClickHouse 서버 또는 프로필에서 이 설정 변경을 금지하는 경우(예: readonly=1 프로필) 다음과 같이 비활성화하십시오:
== null 대신 0 / ""에 대한 명시적 비교를 사용하세요.
데이터 삽입
SaveChanges는 드라이버의 네이티브 InsertBinaryAsync API를 사용합니다 — GZip으로 압축된 RowBinary 인코딩을 사용하므로, 매개변수화된 SQL보다 훨씬 효율적입니다:
Added에서 Unchanged로 전환됩니다.
배치 크기는 설정할 수 있으며(기본값은 1000):
대량 삽입
SaveChanges 대신 BulkInsertAsync를 사용하세요. 이는 DbContext의 확장 메서드로, EF Core의 변경 추적기, identity resolution, 상태 관리를 완전히 우회하고 RowBinary 인코딩과 GZip 압축을 사용해 드라이버의 InsertBinaryAsync를 직접 호출합니다.
따라서 삽입 후 엔터티 추적이 필요 없는 대규모 데이터셋을 로드하는 데 적합합니다:
IEnumerable<T>이든 사용할 수 있으며, 모든 엔터티를 메모리에 로드하지 않고 스트리밍 방식으로 처리합니다. 반환값은 삽입된 행 수입니다. 삽입 후 엔터티는 DbContext에 attach되지 않으므로 Added → Unchanged 상태 전환이 발생하지 않습니다.
열거형
Enum8/Enum16 컬럼은 string 속성 또는 C# enum 타입에 매핑할 수 있습니다. C# 열거형을 사용하면 프로바이더가 열거형과 해당 문자열 표현 간에 자동으로 변환합니다:
사용자 지정 타입 변환
ValueConverter 시스템을 사용하면 사용자 지정 타입을 프로바이더가 이미 지원하는 타입에 매핑할 수 있습니다. 프로바이더는 사용자 지정 타입을 직접 처리하지 않으며, EF Core가 그 경계에서 타입을 변환합니다.
속성별 변환:
컬럼 타입 어노테이션
string, int, DateTime 등과 같은 스칼라 타입의 경우 프로바이더가 ClickHouse 타입을 자동으로 추론합니다. 매개변수화된 타입과 래퍼의 경우에는 ClickHouse 타입을 명시적으로 지정해야 합니다.
데이터 어노테이션(속성) 사용:
OnModelCreating에서 Fluent API 사용:
Array(Nullable(Int32)) 및 LowCardinality(Nullable(String))와 같은 중첩된 래퍼 형식이 지원되며, 프로바이더는 모든 중첩 수준에서 Nullable과 LowCardinality를 자동으로 해제합니다.
Variant 및 Dynamic 컬럼
Variant(T1, T2, ...) 및 Dynamic 컬럼은 .NET의 object에 매핑됩니다. object는 자동 형식 유추를 하기에는 너무 범용적이므로, .HasColumnType()을 사용해 저장 형식을 명시적으로 선언해야 합니다:
string, ulong, ulong[]).
JSON 컬럼
Json 컬럼 타입을 지원하며, System.Text.Json.Nodes.JsonNode(프라이머리) 또는 string(자동 ValueConverter를 통해)으로 매핑됩니다:
SaveChanges와 BulkInsertAsync를 통해 모두 수행할 수 있습니다:
Json 컬럼 유형의 string으로 매핑하세요. 프로바이더에서 ValueConverter를 자동으로 적용합니다:
- JSON 경로 변환 없음 — LINQ의
entity.Data["name"]는 ClickHouse의data.nameSQL 구문으로 변환되지 않습니다. JSON이 아닌 컬럼에 필터를 적용하고, 메모리에서 JSON을 확인하십시오. - NULL 의미 체계 — ClickHouse의 JSON 타입은 NULL 값에 대해 SQL NULL 대신
{}(빈 객체)를 반환합니다. - 정수 정밀도 — ClickHouse JSON은 모든 정수를
Int64로 저장합니다.JsonNode로 읽을 때는GetValue<int>()대신GetValue<long>()를 사용하십시오.
테이블 엔진
ToTable(name, t => ...) Fluent API를 사용해 ClickHouse 테이블 엔진과 엔진별 절을 구성합니다. 엔진을 지정하지 않으면 프로바이더는 엔터티의 기본 키(primary key)에서 도출한 ORDER BY를 사용하며, 기본 테이블 엔진으로 MergeTree를 적용합니다.
엔진 절:
WithOrderBy, WithPartitionBy, WithPrimaryKey, WithSampleBy, WithTtl, WithSettings. 모두 HasXxxEngine()이 반환하는 엔진 빌더에 연결됩니다.
컬럼 수준 기능: HasCodec, HasTtl, HasComment, HasDefault — 모두 마이그레이션에 포함됩니다.
데이터 스키핑 인덱스 — HasIndex(...).HasSkippingIndexType(...)를 통해 사용합니다:
마이그레이션
마이그레이션 제한 사항
마이그레이션 외에도 이 프로바이더는 아직 다음을 지원하지 않습니다:
UPDATE/DELETE- 트랜잭션:
BeginTransaction은 no-op입니다. ClickHouse는 ACID 트랜잭션을 지원하지 않습니다. - JSON 경로 쿼리 변환: LINQ의
entity.Data["key"]는 ClickHouse의data.keySQL 구문으로 변환되지 않습니다. JSON이 아닌 컬럼을 기준으로 필터링하고, 메모리에서 JSON을 확인하십시오.
제한 사항
AggregateFunction 컬럼
AggregateFunction(...) 유형의 컬럼은 직접 쿼리하거나 삽입할 수 없습니다.
삽입하려면: