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

Клиент ClickHouse для C#

Официальный клиент C# для подключения к ClickHouse. Исходный код клиента доступен в репозитории GitHub. Изначально разработан Oleg V. Kozlyuk.

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

  1. Обновите файл .csproj, указав новое имя пакета ClickHouse.Driver и последнюю версию на NuGet.
  2. Замените все вхождения ClickHouse.Client на ClickHouse.Driver в вашей кодовой базе.

Поддерживаемые версии .NET

ClickHouse.Driver поддерживает следующие версии .NET:

  • .NET Framework 4.6.2
  • .NET Framework 4.8
  • .NET Standard 2.1
  • .NET 6.0
  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

Установка

Установите пакет из NuGet:

dotnet add package ClickHouse.Driver

Или с помощью менеджера пакетов NuGet:

Install-Package ClickHouse.Driver

Быстрый старт

using ClickHouse.Driver.ADO;

using (var connection = new ClickHouseConnection("Host=my.clickhouse;Protocol=https;Port=8443;Username=user"))
{
    var version = await connection.ExecuteScalarAsync("SELECT version()");
    Console.WriteLine(version);
}

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

Существует два способа настройки подключения к ClickHouse:

  • Строка подключения: Пары ключ/значение, разделённые точкой с запятой, которые задают хост, учётные данные для аутентификации и другие параметры подключения.
  • Объект ClickHouseClientSettings: Строго типизированный объект конфигурации, который может быть загружен из файлов конфигурации или задан в коде.

Ниже приведён полный список всех параметров, их значений по умолчанию и того, как они влияют на подключение.

Параметры подключения

СвойствоТипЗначение по умолчаниюКлюч строки подключенияОписание
Hoststring"localhost"HostИмя хоста или IP-адрес сервера ClickHouse
Portushort8123 (HTTP) / 8443 (HTTPS)PortНомер порта; по умолчанию выбирается в зависимости от протокола
Usernamestring"default"UsernameИмя пользователя для аутентификации
Passwordstring""PasswordПароль для аутентификации
Databasestring""DatabaseБаза данных по умолчанию; если не задано, используется значение по умолчанию сервера/пользователя
Protocolstring"http"ProtocolПротокол подключения: "http" или "https"
PathstringnullPathПуть в URL для сценариев с обратным прокси (например, /clickhouse)
TimeoutTimeSpan2 минутыTimeoutТаймаут операции (в строке подключения хранится в секундах)

Формат данных и сериализация

СвойствоТипПо умолчаниюКлюч строки подключенияОписание
UseCompressionbooltrueCompressionВключить сжатие gzip при передаче данных
UseCustomDecimalsbooltrueUseCustomDecimalsИспользовать ClickHouseDecimal для произвольной точности; если false, используется .NET decimal (ограничение 128 бит)
UseFormDataParametersboolfalseUseFormDataParametersОтправлять параметры в виде form data вместо URL-строки запроса

Управление сессиями

СвойствоТипЗначение по умолчаниюКлюч строки подключенияОписание
UseSessionboolfalseUseSessionВключить состояние сессий; выполняет запросы последовательно
SessionIdstringnullSessionIdИдентификатор сессии; автоматически генерирует GUID, если null и UseSession имеет значение true
Примечание

Флаг UseSession включает сохранение серверной сессии, что позволяет использовать операторы SET и временные таблицы. Сессии будут сброшены после 60 секунд бездействия (тайм-аут по умолчанию). Время жизни сессии можно увеличить, задав параметры сессии с помощью операторов ClickHouse или конфигурации сервера.

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

Безопасность

СвойствоТипЗначение по умолчаниюКлюч строки подключенияОписание
SkipServerCertificateValidationboolfalseОтключить проверку HTTPS-сертификата; не использовать в продуктивной среде

Конфигурация HTTP‑клиента

СвойствоТипЗначение по умолчаниюКлюч строки подключенияОписание
HttpClientHttpClientnullПользовательский предварительно настроенный экземпляр HttpClient
HttpClientFactoryIHttpClientFactorynullПользовательская фабрика для создания экземпляров HttpClient
HttpClientNamestringnullИмя, используемое HttpClientFactory для создания конкретного клиента

Логирование и отладка

СвойствоТипЗначение по умолчаниюКлюч строки подключенияОписание
LoggerFactoryILoggerFactorynullФабрика логгеров для диагностического логирования
EnableDebugModeboolfalseВключить .NET network tracing (требуется LoggerFactory с уровнем, установленным на Trace); значительное влияние на производительность

Пользовательские настройки и роли

СвойствоТипЗначение по умолчаниюКлюч строки подключенияОписание
CustomSettingsIDictionary<string, object>Пустопрефикс set_*Настройки сервера ClickHouse, см. примечание ниже.
RolesIReadOnlyList<string>ПустоRolesРоли ClickHouse, перечисленные через запятую (например, Roles=admin,reader)
Примечание

При использовании строки подключения для задания пользовательских настроек добавляйте префикс set_, например «set_max_threads=4». При использовании объекта ClickHouseClientSettings префикс set_ добавлять не нужно.

Полный список доступных настроек см. здесь.


Примеры строк подключения

Простое подключение

Host=localhost;Port=8123;Username=default;Password=secret;Database=mydb

С пользовательскими настройками ClickHouse

Host=localhost;set_max_threads=4;set_readonly=1;set_max_memory_usage=10000000000

Использование

Подключение

Чтобы подключиться к ClickHouse, создайте ClickHouseConnection со строкой подключения или объект ClickHouseClientSettings. См. раздел Configuration с описанием доступных параметров.

Информация о вашем сервисе ClickHouse Cloud доступна в консоли ClickHouse Cloud.

Выберите сервис и нажмите Connect:

Кнопка подключения к сервису ClickHouse Cloud

Выберите C#. Ниже будут отображены параметры подключения.

Параметры подключения ClickHouse Cloud для C#

Если вы используете самоуправляемый ClickHouse, параметры подключения задаются вашим администратором ClickHouse.

Подключение с помощью строки подключения:

using ClickHouse.Driver.ADO;

using var connection = new ClickHouseConnection("Host=localhost;Username=default;Password=secret");
await connection.OpenAsync();

Или, используя ClickHouseClientSettings:

var settings = new ClickHouseClientSettings
{
    Host = "localhost",
    Username = "default",
    Password = "secret"
};
using var connection2 = new ClickHouseConnection(settings);
await connection2.OpenAsync();
Примечание
  • ClickHouseConnection представляет собой "сессию" с сервером. При создании соединения выполняется определение доступных возможностей, запрашивается версия сервера (поэтому при открытии есть небольшие накладные расходы), но в целом многократное создание и уничтожение таких объектов является безопасным.
  • Рекомендуемое время жизни подключения — один объект подключения на одну большую "транзакцию", охватывающую несколько запросов. Объект ClickHouseConnection может быть долгоживущим. Есть небольшие накладные расходы при запуске подключения, поэтому не рекомендуется создавать объект подключения для каждого запроса.
  • Если приложение работает с большими объемами транзакций и ему часто требуется создавать/уничтожать объекты ClickHouseConnection, рекомендуется использовать IHttpClientFactory или статический экземпляр HttpClient для управления подключениями.

Создание таблицы

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

using ClickHouse.Driver.ADO;

using (var connection = new ClickHouseConnection(connectionString))
{
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = "CREATE TABLE IF NOT EXISTS default.my_table (id Int64, name String) ENGINE = Memory";
        await command.ExecuteNonQueryAsync();
    }
}

Вставка данных

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

using ClickHouse.Driver.ADO;

using (var connection = new ClickHouseConnection(connectionString))
{
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
        command.AddParameter("id", "Int64", 1);
        command.AddParameter("name", "String", "test");
        command.CommandText = "INSERT INTO default.my_table (id, name) VALUES ({id:Int64}, {name:String})";
        await command.ExecuteNonQueryAsync();
    }
}

Массовая вставка

Используйте ClickHouseBulkCopy для вставки большого количества строк. Он эффективно потоково передаёт данные, используя собственный бинарный построчный формат ClickHouse, работает в параллельном режиме и может разбивать данные на пакеты. Это также позволяет избежать ограничений, связанных с большими наборами параметров, которые вызывают ошибки «URL too long».

Для использования ClickHouseBulkCopy необходимы:

  • Целевое подключение (экземпляр ClickHouseConnection)
  • Имя целевой таблицы (свойство DestinationTableName)
  • Источник данных (IDataReader или IEnumerable<object[]>)
using ClickHouse.Driver.ADO;
using ClickHouse.Driver.Copy;

using var connection = new ClickHouseConnection(connectionString);
await connection.OpenAsync();

using var bulkCopy = new ClickHouseBulkCopy(connection)
{
    DestinationTableName = "default.my_table",
    BatchSize = 100000,
    MaxDegreeOfParallelism = 2
};

await bulkCopy.InitAsync(); // Prepares ClickHouseBulkCopy instance by loading target column types

var values = Enumerable.Range(0, 1000000)
    .Select(i => new object[] { (long)i, "value" + i });

await bulkCopy.WriteToServerAsync(values);
Console.WriteLine($"Rows written: {bulkCopy.RowsWritten}");
Примечание
  • Для оптимальной производительности ClickHouseBulkCopy использует Task Parallel Library (TPL) для обработки пакетов данных с использованием до 4 параллельных задач вставки (это можно настроить).
  • Имена столбцов при необходимости могут быть переданы через свойство ColumnNames, если в исходных данных столбцов меньше, чем в целевой таблице.
  • Настраиваемые параметры: Columns, BatchSize, MaxDegreeOfParallelism.
  • Перед копированием выполняется запрос SELECT * FROM <table> LIMIT 0 для получения информации о структуре целевой таблицы. Типы передаваемых объектов должны разумно соответствовать типам столбцов целевой таблицы.
  • Сессии несовместимы с параллельной вставкой. Подключение, передаваемое в ClickHouseBulkCopy, должно быть без сессий, либо параметр MaxDegreeOfParallelism должен быть установлен в значение 1.

Выполнение запросов SELECT

Выполняйте запросы SELECT с помощью методов ExecuteReader() или ExecuteReaderAsync(). Возвращаемый DbDataReader предоставляет типизированный доступ к столбцам результата через методы, такие как GetInt64(), GetString() и GetFieldValue<T>().

Вызывайте Read(), чтобы перейти к следующей строке. Метод возвращает false, когда строк больше нет. Обращайтесь к столбцам по индексу (с нуля) или по имени столбца.

using ClickHouse.Driver.ADO;
using System.Data;

using (var connection = new ClickHouseConnection(connectionString))
{
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
        command.AddParameter("id", "Int64", 10);
        command.CommandText = "SELECT * FROM default.my_table WHERE id < {id:Int64}";
        using var reader = await command.ExecuteReaderAsync();
        while (reader.Read())
        {
            Console.WriteLine($"select: Id: {reader.GetInt64(0)}, Name: {reader.GetString(1)}");
        }
    }
}

Параметры SQL

В ClickHouse стандартный формат параметров в SQL-запросах — {parameter_name:DataType}.

Примеры:

SELECT {value:Array(UInt16)} as a
SELECT * FROM table WHERE val = {tuple_in_tuple:Tuple(UInt8, Tuple(String, UInt8))}
INSERT INTO table VALUES ({val1:Int32}, {val2:Array(UInt8)})
Примечание

Параметры привязки SQL (bind) передаются как параметры HTTP URI-запроса, поэтому при их чрезмерном количестве может возникнуть исключение «URL too long». Использование ClickHouseBulkInsert позволяет обойти это ограничение.


Идентификатор запроса

Каждый метод, который выполняет запрос, также возвращает query_id в результате. Этот уникальный идентификатор назначается клиентом для каждого запроса и может использоваться для получения данных из таблицы system.query_log (если она включена) или для отмены длительно выполняющихся запросов. При необходимости пользователь может задать идентификатор запроса явно в объекте ClickHouseCommand.

var customQueryId = $"qid-{Guid.NewGuid()}";

using var command = connection.CreateCommand();
command.CommandText = "SELECT version()";
command.QueryId = customQueryId;

var version = await command.ExecuteScalarAsync();
Console.WriteLine($"QueryId: {command.QueryId}");
Совет

Если вы переопределяете параметр QueryId, необходимо обеспечить его уникальность для каждого вызова. Случайный GUID — хороший вариант.


Необработанный стриминг

Можно передавать данные в определённом формате непосредственно, обходя data reader. Это может быть полезно, если вы хотите сохранить данные в файл в нужном формате. Например:

using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM default.my_table LIMIT 100 FORMAT JSONEachRow";
using var result = await command.ExecuteRawResultAsync(CancellationToken.None);
using var stream = await result.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
var json = await reader.ReadToEndAsync();

Вставка из необработанного потока

Используйте InsertRawStreamAsync, чтобы вставлять данные непосредственно из файловых потоков или потоков памяти в форматах, таких как CSV, JSON или любой поддерживаемый формат ClickHouse.

Вставка из CSV‑файла:

await using var fileStream = File.OpenRead("data.csv");

using var response = await connection.InsertRawStreamAsync(
    table: "my_table",
    stream: fileStream,
    format: "CSV",
    columns: ["id", "product", "price"]); // Optional: specify columns
Примечание

См. документацию по настройкам форматов для получения сведений о параметрах, управляющих процессом ингестии данных.


Дополнительные примеры

См. дополнительные практические примеры использования в директории examples репозитория GitHub.

Рекомендации

Время жизни соединения и пул подключений

ClickHouse.Driver внутренне использует System.Net.Http.HttpClient. HttpClient имеет пул подключений для каждой конечной точки (endpoint). В результате:

  • Объект ClickHouseConnection не имеет отображения 1:1 на TCP‑соединения — несколько сеансов работы с базой данных будут мультиплексироваться поверх нескольких TCP‑соединений на один сервер.
  • Объекты ClickHouseConnection могут быть «долго живущими»; реальные TCP‑соединения под ними будут переиспользоваться пулом подключений.
  • Позвольте HttpClient управлять пулом подключений внутренне. Не организуйте пул объектов ClickHouseConnection самостоятельно.
  • Соединения могут оставаться активными после удаления объекта ClickHouseConnection.
  • Это поведение можно настроить, передав пользовательский HttpClientFactory или HttpClient с пользовательским HttpClientHandler.

Для DI‑окружений предусмотрен специальный конструктор ClickHouseConnection(string connectionString, IHttpClientFactory httpClientFactory, string httpClientName = ""), который заставляет ClickHouseConnection запрашивать именованный HTTP‑клиент.

Справочные материалы

При использовании пользовательского HttpClient или HttpClientFactory убедитесь, что PooledConnectionIdleTimeout имеет значение меньше, чем keep_alive_timeout сервера, чтобы избежать ошибок из‑за наполовину закрытых соединений. Значение keep_alive_timeout по умолчанию для развертываний в Cloud — 10 секунд.


Обработка DateTime

  1. По возможности используйте UTC. Храните метки времени в столбцах DateTime('UTC') и используйте DateTimeKind.Utc в коде. Это устраняет неоднозначность, связанную с часовыми поясами.

  2. Используйте DateTimeOffset для явной обработки часовых поясов. Он всегда представляет конкретный момент времени и включает информацию о смещении.

  3. Указывайте часовой пояс в подсказках типа параметров HTTP. При использовании параметров с Unspecified значениями DateTime, записываемыми в столбцы с часовым поясом, отличным от UTC:

    command.AddParameter("dt", value, "DateTime('Europe/Amsterdam')");
    

Асинхронные вставки

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

Включите асинхронные вставки через CustomSettings или строку подключения:

// Using CustomSettings
var settings = new ClickHouseClientSettings("Host=localhost");
settings.CustomSettings["async_insert"] = 1;
settings.CustomSettings["wait_for_async_insert"] = 1; // Recommended: wait for flush acknowledgment

// Or via connection string
// "Host=localhost;set_async_insert=1;set_wait_for_async_insert=1"

Два режима (управляются параметром wait_for_async_insert):

ModeBehaviorUse case
wait_for_async_insert=1Вставка (INSERT) завершается после сброса данных на диск. Ошибки возвращаются клиенту.Рекомендуется для большинства нагрузок
wait_for_async_insert=0Вставка (INSERT) завершается сразу после буферизации данных. Нет гарантии сохранения данных.Только когда допустима потеря данных
Примечание

При wait_for_async_insert=0 ошибки возникают только во время flush и не могут быть однозначно сопоставлены с исходной вставкой. Клиент также не создает обратного давления, что повышает риск перегрузки сервера.

Ключевые настройки:

SettingDescription
async_insert_max_data_sizeВыполнить flush, когда буфер достигает указанного размера (в байтах)
async_insert_busy_timeout_msВыполнить flush по истечении указанного тайм-аута (в миллисекундах)
async_insert_max_query_numberВыполнить flush после накопления указанного числа запросов

Сессии

Включайте сессии только тогда, когда вам нужны серверные возможности с сохранением состояния, например:

  • Временные таблицы (CREATE TEMPORARY TABLE)
  • Сохранение контекста запроса между несколькими командами
  • Настройки на уровне сессии (SET max_threads = 4)

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

var settings = new ClickHouseClientSettings
{
    Host = "localhost",
    UseSession = true,
    SessionId = "my-session", // Optional -- will be auto-generated if not provided
};

await using var connection = new ClickHouseConnection(settings);
await connection.OpenAsync();

await using var cmd1 = connection.CreateCommand("CREATE TEMPORARY TABLE temp_ids (id UInt64)");
await cmd1.ExecuteNonQueryAsync();

await using var cmd2 = connection.CreateCommand("INSERT INTO temp_ids VALUES (1), (2), (3)");
await cmd2.ExecuteNonQueryAsync();

await using var cmd3 = connection.CreateCommand("SELECT * FROM users WHERE id IN (SELECT id FROM temp_ids)");
await using var reader = await cmd3.ExecuteReaderAsync();

Поддерживаемые типы данных

ClickHouse.Driver поддерживает все типы данных ClickHouse. В приведённых ниже таблицах показаны сопоставления между типами ClickHouse и нативными типами .NET при чтении данных из базы данных.

Сопоставление типов: чтение из ClickHouse

Целочисленные типы

Тип в ClickHouseТип в .NET
Int8sbyte
UInt8byte
Int16short
UInt16ushort
Int32int
UInt32uint
Int64long
UInt64ulong
Int128BigInteger
UInt128BigInteger
Int256BigInteger
UInt256BigInteger

Типы с плавающей запятой

Тип ClickHouseТип .NET
Float32float
Float64double
BFloat16float

Типы Decimal

Тип ClickHouseТип .NET
Decimal(P, S)decimal / ClickHouseDecimal
Decimal32(S)decimal / ClickHouseDecimal
Decimal64(S)decimal / ClickHouseDecimal
Decimal128(S)decimal / ClickHouseDecimal
Decimal256(S)decimal / ClickHouseDecimal
Примечание

Преобразование типов Decimal управляется настройкой UseCustomDecimals.


Булев тип

Тип ClickHouseТип .NET
Boolbool

Строковые типы

Тип ClickHouseТип .NET
Stringstring
FixedString(N)byte[]

Типы даты и времени

ClickHouse Type.NET Type
DateDateTime
Date32DateTime
DateTimeDateTime
DateTime32DateTime
DateTime64DateTime
TimeTimeSpan
Time64TimeSpan

ClickHouse хранит значения DateTime и DateTime64 во внутреннем представлении как Unix-временные метки (Unix timestamps — секунды или доли секунды, прошедшие с начала эпохи Unix). Хотя хранение всегда ведётся в UTC, у столбцов может быть привязан часовой пояс, который влияет на то, как значения отображаются и интерпретируются.

При чтении значений DateTime свойство DateTime.Kind устанавливается на основе часового пояса столбца:

Column DefinitionReturned DateTime.KindNotes
DateTime('UTC')UtcЯвный часовой пояс UTC
DateTime('Europe/Amsterdam')UnspecifiedПрименяется часовой пояс со смещением
DateTimeUnspecifiedЛокальное (wall-clock) время сохраняется как есть

Для столбцов с часовым поясом, отличным от UTC, возвращаемое значение DateTime представляет локальное (wall-clock) время в соответствующем часовом поясе. Используйте ClickHouseDataReader.GetDateTimeOffset() для получения DateTimeOffset с корректным смещением для этого часового пояса:

var reader = (ClickHouseDataReader)await connection.ExecuteReaderAsync(
    "SELECT toDateTime('2024-06-15 14:30:00', 'Europe/Amsterdam')");
reader.Read();

var dt = reader.GetDateTime(0);    // 2024-06-15 14:30:00, Kind=Unspecified
var dto = reader.GetDateTimeOffset(0); // 2024-06-15 14:30:00 +02:00 (CEST)

Для столбцов без явного часового пояса (т.е. DateTime вместо DateTime('Europe/Amsterdam')) драйвер возвращает DateTime с Kind=Unspecified. Это позволяет сохранить «настенное» время в точности в том виде, как оно хранится, не делая предположений о часовом поясе.

Если вам требуется поведение с учетом часового пояса для столбцов без явного часового пояса, то:

  1. Используйте явные часовые пояса в определениях столбцов: DateTime('UTC') или DateTime('Europe/Amsterdam')
  2. Устанавливайте нужный часовой пояс самостоятельно после чтения данных.

Другие типы

Тип ClickHouseТип .NET
UUIDGuid
IPv4IPAddress
IPv6IPAddress
NothingDBNull
DynamicСм. примечание
JsonJsonObject
Array(T)T[]
Tuple(T1, T2, ...)Tuple<T1, T2, ...> / LargeTuple
Map(K, V)Dictionary<K, V>
Nullable(T)T?
Enum8string
Enum16string
LowCardinality(T)Такой же, как T
SimpleAggregateFunctionТакой же, как базовый тип
Nested(...)Tuple[]
Variant(T1, T2, ...)См. примечание
QBit(T, dimension)T[]
Примечание

Типы Dynamic и Variant будут преобразованы в тип, соответствующий фактическому базовому типу в каждой строке.


Типы геометрии

Тип ClickHouseТип .NET
PointTuple<double, double>
RingTuple<double, double>[]
LineStringTuple<double, double>[]
PolygonRing[]
MultiLineStringLineString[]
MultiPolygonPolygon[]
GeometryСм. примечание
Примечание

Тип Geometry — это тип Variant, который может содержать любой из геометрических типов. Он будет преобразован в соответствующий тип.


Сопоставление типов: запись в ClickHouse

При вставке данных драйвер преобразует типы .NET в соответствующие типы ClickHouse. В таблицах ниже показано, какие типы .NET поддерживаются для каждого типа столбца ClickHouse.

Целочисленные типы

Тип ClickHouseПринимаемые типы .NETПримечания
Int8sbyte, любой, совместимый с Convert.ToSByte()
UInt8byte, любой, совместимый с Convert.ToByte()
Int16short, любой, совместимый с Convert.ToInt16()
UInt16ushort, любой, совместимый с Convert.ToUInt16()
Int32int, любой, совместимый с Convert.ToInt32()
UInt32uint, любой, совместимый с Convert.ToUInt32()
Int64long, любой, совместимый с Convert.ToInt64()
UInt64ulong, любой, совместимый с Convert.ToUInt64()
Int128BigInteger, decimal, double, float, int, uint, long, ulong, любой, совместимый с Convert.ToInt64()
UInt128BigInteger, decimal, double, float, int, uint, long, ulong, любой, совместимый с Convert.ToInt64()
Int256BigInteger, decimal, double, float, int, uint, long, ulong, любой, совместимый с Convert.ToInt64()
UInt256BigInteger, decimal, double, float, int, uint, long, ulong, любой, совместимый с Convert.ToInt64()

Типы с плавающей запятой

Тип ClickHouseПоддерживаемые типы .NETПримечания
Float32float, любой тип, совместимый с Convert.ToSingle()
Float64double, любой тип, совместимый с Convert.ToDouble()
BFloat16float, любой тип, совместимый с Convert.ToSingle()Усекает значение до 16-битного формата brain float

Логический тип

Тип ClickHouseДопустимые типы .NETПримечания
Boolbool

Строковые типы

Тип ClickHouseДопустимые типы .NETПримечания
Stringstring, любой тип, совместимый с Convert.ToString()
FixedString(N)string, byte[]String кодируется в UTF-8 и дополняется/усекается; массив byte[] должен содержать ровно N байт

Типы даты и времени

Тип ClickHouseДопустимые типы .NETПримечания
DateDateTime, DateTimeOffset, DateOnly, типы NodaTimeПреобразуется в количество Unix-дней как UInt16
Date32DateTime, DateTimeOffset, DateOnly, типы NodaTimeПреобразуется в количество Unix-дней как Int32
DateTimeDateTime, DateTimeOffset, DateOnly, типы NodaTimeСм. подробности ниже
DateTime32DateTime, DateTimeOffset, DateOnly, типы NodaTimeТо же, что и DateTime
DateTime64DateTime, DateTimeOffset, DateOnly, типы NodaTimeТочность зависит от параметра Scale
TimeTimeSpan, intОграничивается диапазоном ±999:59:59; значения int интерпретируются как секунды
Time64TimeSpan, decimal, double, float, int, long, stringСтрока разбирается как [-]HHH:MM:SS[.fraction]; ограничивается до ±999:59:59.999999999

Драйвер учитывает DateTime.Kind при записи значений:

DateTime.KindПоведение
UtcМомент времени сохраняется без изменений
LocalПреобразуется в UTC с использованием часового пояса системы; момент сохраняется
UnspecifiedРассматривается как локальное время в часовом поясе целевого столбца

Значения DateTimeOffset всегда сохраняют точный момент времени.

Пример: DateTime в UTC (момент сохраняется)

var utcTime = new DateTime(2024, 1, 15, 12, 0, 0, DateTimeKind.Utc);
// Stored as 12:00 UTC
// Read from DateTime('Europe/Amsterdam') column: 13:00 (UTC+1)
// Read from DateTime('UTC') column: 12:00 UTC

Пример: неопределённый DateTime (локальное «настенное» время)

var wallClock = new DateTime(2024, 1, 15, 14, 30, 0, DateTimeKind.Unspecified);
// Written to DateTime('Europe/Amsterdam') column: stored as 14:30 Amsterdam time
// Read back from DateTime('Europe/Amsterdam') column: 14:30

Рекомендация: для наиболее простого и предсказуемого поведения используйте DateTimeKind.Utc или DateTimeOffset для всех операций с типом DateTime. Это позволит вашему коду работать одинаково независимо от часового пояса сервера, клиента или часового пояса столбца.

HTTP-параметры vs bulk copy

Существует существенное отличие между привязкой HTTP-параметров и bulk copy при записи значений DateTime с Kind Unspecified:

Bulk Copy знает часовой пояс целевого столбца и корректно интерпретирует значения Unspecified в этом часовом поясе.

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

// CORRECT: Timezone in type hint
command.AddParameter("dt", myDateTime, "DateTime('Europe/Amsterdam')");
command.CommandText = "INSERT INTO table (dt_amsterdam) VALUES ({dt:DateTime('Europe/Amsterdam')})";

// INCORRECT: Without timezone hint, interpreted as UTC
command.AddParameter("dt", myDateTime);
command.CommandText = "INSERT INTO table (dt_amsterdam) VALUES ({dt:DateTime})";
// String value "2024-01-15 14:30:00" interpreted as UTC, not Amsterdam time!
DateTime.KindЦелевой столбецHTTP-параметр (с указанием часового пояса)HTTP-параметр (без указания часового пояса)Массовое копирование
UtcUTCМомент сохраняетсяМомент сохраняетсяМомент сохраняется
UtcEurope/AmsterdamМомент сохраняетсяМомент сохраняетсяМомент сохраняется
LocalЛюбойМомент сохраняетсяМомент сохраняетсяМомент сохраняется
UnspecifiedUTCИнтерпретируется как UTCИнтерпретируется как UTCИнтерпретируется как UTC
UnspecifiedEurope/AmsterdamИнтерпретируется как время АмстердамаИнтерпретируется как UTCИнтерпретируется как временем Амстердама

Типы Decimal

Тип ClickHouseПоддерживаемые типы .NETПримечания
Decimal(P,S)decimal, ClickHouseDecimal, любой тип, совместимый с Convert.ToDecimal()Выбрасывает исключение OverflowException, если превышена точность
Decimal32decimal, ClickHouseDecimal, любой тип, совместимый с Convert.ToDecimal()Максимальная точность 9
Decimal64decimal, ClickHouseDecimal, любой тип, совместимый с Convert.ToDecimal()Максимальная точность 18
Decimal128decimal, ClickHouseDecimal, любой тип, совместимый с Convert.ToDecimal()Максимальная точность 38
Decimal256decimal, ClickHouseDecimal, любой тип, совместимый с Convert.ToDecimal()Максимальная точность 76

Другие типы

Тип ClickHouseПринимаемые типы .NETПримечания
UUIDGuid, stringСтрока парсится как Guid
IPv4IPAddress, stringДолжен быть IPv4; строка парсится через IPAddress.Parse()
IPv6IPAddress, stringДолжен быть IPv6; строка парсится через IPAddress.Parse()
NothingЛюбой типНичего не записывает (операция no-op)
DynamicНе поддерживается (выбрасывает NotImplementedException)
Jsonstring, JsonObject, любой объектСтрока парсится как JSON; объекты сериализуются через JsonSerializer
Array(T)IList, nullПри значении null записывается пустой массив
Tuple(T1, T2, ...)ITuple, IListКоличество элементов должно соответствовать арности кортежа
Map(K, V)IDictionary
Nullable(T)null, DBNull или типы, принимаемые TПеред значением записывается байт флага null
Enum8string, sbyte, числовые типыСтроковое значение ищется в словаре enum
Enum16string, short, числовые типыСтроковое значение ищется в словаре enum
LowCardinality(T)Типы, принимаемые TДелегирует базовому типу
SimpleAggregateFunctionТипы, принимаемые базовым типомДелегирует базовому типу
Nested(...)IList кортежейКоличество элементов должно соответствовать количеству полей
Variant(T1, T2, ...)Значение, соответствующее одному из T1, T2, ...Выбрасывает ArgumentException, если нет совпадения типа
QBit(T, dim)IListДелегирует типу Array; размерность — только метаданные

Геометрические типы

Тип ClickHouseДопустимые типы .NETПримечания
PointSystem.Drawing.Point, ITuple, IList (2 элемента)
RingIList из Point
LineStringIList из Point
PolygonIList из Ring
MultiLineStringIList из LineString
MultiPolygonIList из Polygon
GeometryЛюбой из указанных выше геометрических типовОбобщающий вариант всех геометрических типов

Запись не поддерживается

Тип ClickHouseПримечания
DynamicВызывает исключение NotImplementedException
AggregateFunctionВызывает исключение AggregateFunctionException

Обработка вложенных типов

Вложенные типы ClickHouse (Nested(...)) можно читать и записывать с использованием семантики массивов.

CREATE TABLE test.nested (
    id UInt32,
    params Nested (param_id UInt8, param_val String)
) ENGINE = Memory
using var bulkCopy = new ClickHouseBulkCopy(connection)
{
    DestinationTableName = "test.nested"
};

var row1 = new object[] { 1, new[] { 1, 2, 3 }, new[] { "v1", "v2", "v3" } };
var row2 = new object[] { 2, new[] { 4, 5, 6 }, new[] { "v4", "v5", "v6" } };

await bulkCopy.WriteToServerAsync(new[] { row1, row2 });

Журналирование и диагностика

Клиент ClickHouse для .NET интегрируется с абстракциями логирования Microsoft.Extensions.Logging, предоставляя легковесное журналирование, подключаемое по желанию. При его включении драйвер генерирует структурированные сообщения о событиях жизненного цикла подключения, выполнении команд, транспортных операциях и массовой загрузке данных. Журналирование полностью необязательно — приложения, которые не настраивают логгер, продолжают работать без дополнительных накладных расходов.

Быстрый старт

Использование ClickHouseConnection

using ClickHouse.Driver.ADO;
using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
        .AddConsole()
        .SetMinimumLevel(LogLevel.Information);
});

var settings = new ClickHouseClientSettings("Host=localhost;Port=8123")
{
    LoggerFactory = loggerFactory
};

await using var connection = new ClickHouseConnection(settings);
await connection.OpenAsync();

Использование appsettings.json

Вы можете настроить уровни логирования с помощью стандартной системы конфигурации .NET:

using ClickHouse.Driver.ADO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

var configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json")
    .Build();

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
        .AddConfiguration(configuration.GetSection("Logging"))
        .AddConsole();
});

var settings = new ClickHouseClientSettings("Host=localhost;Port=8123")
{
    LoggerFactory = loggerFactory
};

await using var connection = new ClickHouseConnection(settings);
await connection.OpenAsync();

Использование конфигурации в оперативной памяти

Вы также можете настроить детализацию логирования по категориям прямо в коде:

using ClickHouse.Driver.ADO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

var categoriesConfiguration = new Dictionary<string, string>
{
    { "LogLevel:Default", "Warning" },
    { "LogLevel:ClickHouse.Driver.Connection", "Information" },
    { "LogLevel:ClickHouse.Driver.Command", "Debug" }
};

var config = new ConfigurationBuilder()
    .AddInMemoryCollection(categoriesConfiguration)
    .Build();

using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
        .AddConfiguration(config)
        .AddSimpleConsole();
});

var settings = new ClickHouseClientSettings("Host=localhost;Port=8123")
{
    LoggerFactory = loggerFactory
};

await using var connection = new ClickHouseConnection(settings);
await connection.OpenAsync();

Категории и источники

Драйвер использует отдельные категории, чтобы вы могли точно настраивать уровни логирования для каждого компонента:

CategorySourceHighlights
ClickHouse.Driver.ConnectionClickHouseConnectionЖизненный цикл соединения, выбор фабрики HTTP‑клиента, открытие/закрытие соединения, управление сессиями.
ClickHouse.Driver.CommandClickHouseCommandНачало и завершение выполнения запроса, замер времени, идентификаторы запросов, статистика сервера и сведения об ошибках.
ClickHouse.Driver.TransportClickHouseConnectionНизкоуровневые потоковые HTTP‑запросы, флаги сжатия, коды статуса ответа и сбои транспортного уровня.
ClickHouse.Driver.BulkCopyClickHouseBulkCopyЗагрузка метаданных, пакетные операции, количество строк и завершение отправки.
ClickHouse.Driver.NetTraceTraceHelperОтслеживание сетевых операций, только при включённом режиме отладки.

Пример: диагностика неполадок подключения

{
    "Logging": {
        "LogLevel": {
            "ClickHouse.Driver.Connection": "Trace",
            "ClickHouse.Driver.Transport": "Trace"
        }
    }
}

В журнал будет записано:

  • выбор фабрики HTTP-клиента (пул по умолчанию по сравнению с одиночным подключением)
  • конфигурация HTTP-обработчика (SocketsHttpHandler или HttpClientHandler)
  • настройки пула подключений (MaxConnectionsPerServer, PooledConnectionLifetime и т. д.)
  • параметры тайм-аутов (ConnectTimeout, Expect100ContinueTimeout и т. д.)
  • конфигурация SSL/TLS
  • события открытия и закрытия подключений
  • отслеживание идентификаторов сессий

Режим отладки: трассировка сети и диагностика

Чтобы упростить диагностику сетевых проблем, библиотека драйвера предоставляет вспомогательный инструмент, позволяющий включить низкоуровневую трассировку внутренних сетевых механизмов .NET. Чтобы включить её, необходимо передать LoggerFactory с уровнем Trace и установить EnableDebugMode в значение true (или включить её вручную через класс ClickHouse.Driver.Diagnostic.TraceHelper). События будут логироваться в категорию ClickHouse.Driver.NetTrace. Предупреждение: это приведёт к генерации чрезвычайно подробных логов и повлияет на производительность. Не рекомендуется включать режим отладки в продуктивной среде.

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
        .AddConsole()
        .SetMinimumLevel(LogLevel.Trace); // Must be Trace level to see network events
});

var settings = new ClickHouseClientSettings()
{
    LoggerFactory = loggerFactory,
    EnableDebugMode = true,  // Enable low-level network tracing
};

OpenTelemetry

Драйвер предоставляет встроенную поддержку распределённого трейсинга OpenTelemetry через API .NET System.Diagnostics.Activity. При его включении драйвер генерирует спаны для операций с базой данных, которые могут быть экспортированы в обсервабилити-бэкенды, такие как Jaeger или сам ClickHouse (через OpenTelemetry Collector).

Включение трассировки

В приложениях ASP.NET Core добавьте ActivitySource драйвера ClickHouse в конфигурацию OpenTelemetry:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddSource(ClickHouseDiagnosticsOptions.ActivitySourceName)  // Subscribe to ClickHouse driver spans
        .AddAspNetCoreInstrumentation()
        .AddOtlpExporter());             // Or AddJaegerExporter(), etc.

Для консольных приложений, тестирования или ручной настройки:

using OpenTelemetry;
using OpenTelemetry.Trace;

var tracerProvider = Sdk.CreateTracerProviderBuilder()
    .AddSource(ClickHouseDiagnosticsOptions.ActivitySourceName)
    .AddConsoleExporter()
    .Build();

Атрибуты спана

Каждый спан включает стандартные атрибуты базы данных OpenTelemetry, а также специфичные для ClickHouse статистические данные по запросу, которые можно использовать для отладки.

АтрибутОписание
db.systemВсегда "clickhouse"
db.nameИмя базы данных
db.userИмя пользователя
db.statementSQL-запрос (если включено)
db.clickhouse.read_rowsКоличество строк, прочитанных запросом
db.clickhouse.read_bytesКоличество байт, прочитанных запросом
db.clickhouse.written_rowsКоличество строк, записанных запросом
db.clickhouse.written_bytesКоличество байт, записанных запросом
db.clickhouse.elapsed_nsВремя выполнения на стороне сервера в наносекундах

Параметры конфигурации

Настройте поведение трассировки с помощью ClickHouseDiagnosticsOptions:

using ClickHouse.Driver.Diagnostic;

// Include SQL statements in spans (default: false for security)
ClickHouseDiagnosticsOptions.IncludeSqlInActivityTags = true;

// Truncate long SQL statements (default: 1000 characters)
ClickHouseDiagnosticsOptions.StatementMaxLength = 500;
Примечание

Включение IncludeSqlInActivityTags может привести к раскрытию конфиденциальных данных в ваших трассировках. Используйте с осторожностью в производственных средах.

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

При подключении к ClickHouse по HTTPS вы можете по‑разному настроить работу TLS/SSL.

Пользовательская проверка сертификатов

Для продакшн-сред, где требуется собственная логика проверки сертификатов, используйте свой HttpClient с настроенным обработчиком ServerCertificateCustomValidationCallback:

using System.Net;
using System.Net.Security;
using ClickHouse.Driver.ADO;

var handler = new HttpClientHandler
{
    // Required when compression is enabled (default)
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,

    ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
    {
        // Example: Accept a specific certificate thumbprint
        if (cert?.Thumbprint == "YOUR_EXPECTED_THUMBPRINT")
            return true;

        // Example: Accept certificates from a specific issuer
        if (cert?.Issuer.Contains("YourOrganization") == true)
            return true;

        // Default: Use standard validation
        return sslPolicyErrors == SslPolicyErrors.None;
    },
};

var httpClient = new HttpClient(handler) { Timeout = TimeSpan.FromMinutes(5) };

var settings = new ClickHouseClientSettings
{
    Host = "my.clickhouse.server",
    Protocol = "https",
    HttpClient = httpClient,
};

using var connection = new ClickHouseConnection(settings);
await connection.OpenAsync();
Примечание

Важные замечания при передаче собственного HttpClient

  • Автоматическая декомпрессия: необходимо включить AutomaticDecompression, если сжатие не отключено (по умолчанию сжатие включено).
  • Тайм-аут простоя: установите PooledConnectionIdleTimeout меньше, чем keep_alive_timeout сервера (10 секунд для ClickHouse Cloud), чтобы избежать ошибок подключения из‑за полуоткрытых соединений.

Поддержка ORM

Dapper

ClickHouse.Driver можно использовать с Dapper, но анонимные объекты при этом не поддерживаются.

Рабочий пример:

connection.QueryAsync<string>(
    "SELECT {p1:Int32}",
    new Dictionary<string, object> { { "p1", 42 } }
);

Не поддерживается:

connection.QueryAsync<string>(
    "SELECT {p1:Int32}",
    new { p1 = 42 }
);

Linq2db

Этот драйвер совместим с linq2db — легковесным ORM и провайдером LINQ для .NET. Подробную документацию см. на сайте проекта.

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

Создайте объект DataConnection с использованием провайдера ClickHouse:

using LinqToDB;
using LinqToDB.Data;
using LinqToDB.DataProvider.ClickHouse;

var connectionString = "Host=localhost;Port=8123;Database=default";
var options = new DataOptions()
    .UseClickHouse(connectionString, ClickHouseProvider.ClickHouseDriver);

await using var db = new DataConnection(options);

Сопоставления таблиц могут задаваться с помощью атрибутов или fluent‑конфигурации. Если имена ваших классов и свойств в точности совпадают с именами таблиц и столбцов, никакая конфигурация не требуется:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

Выполнение запросов:

await using var db = new DataConnection(options);

var products = await db.GetTable<Product>()
    .Where(p => p.Price > 100)
    .OrderByDescending(p => p.Name)
    .ToListAsync();

Массовое копирование (Bulk Copy):

Используйте BulkCopyAsync для эффективной массовой вставки данных.

await using var db = new DataConnection(options);
var table = db.GetTable<Product>();

var options = new BulkCopyOptions
{
    MaxBatchSize = 100000,
    MaxDegreeOfParallelism = 1,
    WithoutSession = true
};

await table.BulkCopyAsync(options, products);

Entity framework core

Entity Framework Core на данный момент не поддерживается.

Ограничения

Столбцы типа AggregateFunction

Столбцы типа AggregateFunction(...) нельзя напрямую использовать в запросах или при вставке данных.

Для вставки:

INSERT INTO t VALUES (uniqState(1));

Чтобы выбрать:

SELECT uniqMerge(c) FROM t;