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

Клиент 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);
}

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

using Dapper;
using ClickHouse.Driver.ADO;

using (var connection = new ClickHouseConnection("Host=my.clickhouse"))
{
    var result = await connection.QueryAsync<string>("SELECT name FROM system.databases");
    Console.WriteLine(string.Join('\n', result));
}

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

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

ПараметрОписаниеЗначение по умолчанию
HostАдрес сервера ClickHouselocalhost
PortПорт сервера ClickHouse8123 или 8443 (в зависимости от Protocol)
DatabaseНачальная база данныхdefault
UsernameИмя пользователя для аутентификацииdefault
PasswordПароль для аутентификации(пусто)
ProtocolПротокол подключения (http или https)http
CompressionВключает сжатие Gziptrue
UseSessionВключает постоянную серверную сессиюfalse
SessionIdПользовательский идентификатор сессииСлучайный GUID
TimeoutHTTP‑тайм-аут (в секундах)120
UseServerTimezoneИспользовать часовой пояс сервера для столбцов datetimetrue
UseCustomDecimalsИспользовать ClickHouseDecimal для десятичных чиселfalse

Пример: Host=clickhouse;Port=8123;Username=default;Password=;Database=default

Sessions

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

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


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

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

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

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

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

  • ClickHouseConnection представляет собой «сеанс» с сервером. Он выполняет обнаружение возможностей, запрашивая версию сервера (что вносит небольшие накладные расходы при открытии), но в целом безопасно многократно создавать и уничтожать такие объекты.
  • Рекомендуемый срок жизни соединения — один объект соединения на одну крупную «транзакцию», охватывающую несколько запросов. Поскольку при установке соединения есть небольшие накладные расходы, не рекомендуется создавать объект соединения для каждого запроса.
  • Если приложение обрабатывает большие объёмы транзакций и ему необходимо часто создавать и уничтожать объекты ClickHouseConnection, рекомендуется использовать IHttpClientFactory или статический экземпляр HttpClient для управления соединениями.

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

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

using ClickHouse.Driver.ADO;

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

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

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

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

using ClickHouse.Driver.ADO;

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

    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})";
        command.ExecuteNonQuery();
    }
}

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

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

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

using var connection = new ClickHouseConnection(connectionString);
connection.Open();

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

await bulkCopy.InitAsync(); // Подготавливает экземпляр ClickHouseBulkCopy, загружая типы целевых столбцов

var values = Enumerable.Range(0, 1000000)
    .Select(i => new object[] { (long)i, "значение" + i });

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

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

Выполните запросы SELECT и обработайте результаты:

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

using (var connection = new ClickHouseConnection(connectionString))
{
    connection.Open();
    
    using (var command = connection.CreateCommand())
    {
        command.AddParameter("id", "Int64", 10);
        command.CommandText = "SELECT * FROM default.my_table WHERE id < {id:Int64}";
        using var reader = command.ExecuteReader();
        while (reader.Read())
        {
            Console.WriteLine($"выборка: Id: {reader.GetInt64(0)}, Имя: {reader.GetString(1)}");
        }
    }
}

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

using var command = connection.CreateCommand();
command.Text = "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 = reader.ReadToEnd();

Поддержка вложенных столбцов

Вложенные типы 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 });

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

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

Для вставки:

INSERT INTO t VALUES (uniqState(1));

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

SELECT uniqMerge(c) FROM t;

Параметры SQL

При передаче параметров в запрос следует использовать форматирование параметров ClickHouse в следующем формате:

{<name>:<data type>}

Примеры:

SELECT {value:Array(UInt16)} as value
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».
  • Для вставки большого объёма записей рассмотрите использование механизма пакетной вставки (Bulk Insert).

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

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

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

  • Boolbool

Числовые типы

Знаковые целые типы:

  • Int8sbyte
  • Int16short
  • Int32int
  • Int64long
  • Int128BigInteger
  • Int256BigInteger

Беззнаковые целые типы:

  • UInt8byte
  • UInt16ushort
  • UInt32uint
  • UInt64ulong
  • UInt128BigInteger
  • UInt256BigInteger

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

  • Float32float
  • Float64double

Десятичные типы:

  • Decimaldecimal
  • Decimal32decimal
  • Decimal64decimal
  • Decimal128decimal
  • Decimal256BigDecimal

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

  • Stringstring
  • FixedStringstring

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

  • DateDateTime
  • Date32DateTime
  • DateTimeDateTime
  • DateTime32DateTime
  • DateTime64DateTime

Типы сетей

  • IPv4IPAddress
  • IPv6IPAddress

Географические типы

  • PointTuple
  • RingArray of Points
  • PolygonArray of Rings

Составные типы данных

  • Array(T)Массив любого типа
  • Tuple(T1, T2, ...)Кортеж любых типов
  • Nullable(T)Nullable-тип на основе любого типа
  • Map(K, V)Словарь<K, V>

Обработка DateTime

ClickHouse.Driver корректно обрабатывает часовые пояса и свойство DateTime.Kind. В частности:

  • Значения DateTime возвращаются в UTC. Пользователь затем может преобразовать их самостоятельно или использовать метод ToLocalTime() для экземпляра DateTime.
  • При вставке данные типа DateTime обрабатываются следующим образом:
    • UTC DateTime вставляются «как есть», поскольку ClickHouse хранит их в UTC.
    • Local DateTime преобразуются в UTC в соответствии с локальными настройками часового пояса пользователя.
    • Unspecified DateTime считаются находящимися в часовом поясе целевого столбца и, следовательно, преобразуются в UTC в соответствии с этим часовым поясом.
  • Для столбцов без указанного часового пояса по умолчанию используется часовой пояс клиента (устаревшее поведение). Вместо этого можно использовать флаг UseServerTimezone в строке подключения, чтобы применять часовой пояс сервера.

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

Клиент 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Загрузка метаданных, пакетные операции, количество строк и завершение отправки.

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

{
    "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). Предупреждение: это приведёт к генерации чрезвычайно подробных логов и повлияет на производительность. Не рекомендуется включать режим отладки в боевой (production) среде.

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder
        .AddConsole()
        .SetMinimumLevel(LogLevel.Trace); // Необходим уровень Trace для просмотра сетевых событий
});

var settings = new ClickHouseClientSettings()
{
    LoggerFactory = loggerFactory,
    EnableDebugMode = true,  // Включить низкоуровневую трассировку сетевых событий
};

Поддержка 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 }
);