Skip to main content

Экспериментальный статус

API нативного TCP-клиента является экспериментальным. Его публичный интерфейс может измениться в одном из будущих релизов.
Основной клиент, источник данных, сеанс, интерфейсы операций и расширения для внедрения зависимостей вызывают предупреждение компилятора CHTCP0001. Чтобы подавить это предупреждение на время ознакомления с API, добавьте его в NoWarn в файле проекта:
Отключайте это предупреждение, только если вы готовы к тому, что в одном из будущих релизов код, использующий этот API, может потребовать изменений.

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

Нативный TCP-клиент поддерживает следующие версии .NET:
  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

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

TCP-клиент официально поддерживает две последние LTS-версии и три последних релиза сервера ClickHouse.

Установка

Нативный TCP-клиент входит в пакет ClickHouse.Driver. Установите его из NuGet:
Или с помощью NuGet Package Manager:

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

Переход с HTTP-клиента

TCP API не связан с IClickHouseClient: TCP нельзя выбрать в качестве транспортного протокола для ClickHouseClient или ClickHouseConnection. Большинство SQL-запросов можно использовать без изменений, но потребуется создать ClickHouseTcpClient и заменить каждую операцию её аналогом для TCP.

Соответствия методов

Например, цикл чтения через HTTP-клиент:
превращается в асинхронный поток строк по TCP:

Параметры и аргументы

HTTP-версии QueryOptions и ClickHouseParameterCollection не взаимозаменяемы с их аналогами для TCP. Перенесите значения в ClickHouseTcpQueryOptions: в нём настройки задаются строками, а аргументы входят в объект параметров:
TCP-клиент поддерживает плейсхолдеры {name:Type}, встроенные в ClickHouse, но не преобразует плейсхолдеры @name в стиле ADO.NET. Кроме того, для отдельных запросов нельзя задать базу данных, роль, Bearer-токен или пользовательский заголовок; также не поддерживаются средство разрешения или форматтер параметров и преобразователь данных при чтении. Подробнее о возможностях, доступных только через HTTP, см. в разделе «Ограничения».

Время жизни соединения и результата

Оба высокоуровневых клиента потокобезопасны, поэтому их следует использовать повторно. TCP-клиент управляет постоянными соединениями по нативному протоколу, а не HTTP-соединениями, реализует IAsyncDisposable и обычно должен асинхронно освобождаться при завершении работы приложения. Строки, возвращаемые QueryAsync, принадлежат вызывающему коду. Блоки StreamAsync и их столбцы не принадлежат вызывающему коду и доступны только во время текущей итерации; значения, которые понадобятся позже, необходимо скопировать. Всегда завершайте перебор или освобождайте его ресурсы, чтобы соединение вернулось в пул. Используйте OpenSessionAsync, если несколько операций должны выполняться через одно соединение с сохранением состояния сеанса. Полный пример миграции с HTTP на TCP показывает, как оба клиента работают с одной таблицей.

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

Нативный TCP-клиент можно настроить двумя способами:
  • Строка подключения: пары «ключ — значение», разделённые точкой с запятой и задающие конечную точку сервера, учётные данные для аутентификации и другие параметры соединения.
  • Объект ClickHouseTcpClientOptions: строго типизированный неизменяемый объект конфигурации, задаваемый в коде.
Передайте строку подключения или объект конфигурации конструктору ClickHouseTcpClient. Используйте ClickHouseTcpConnectionStringBuilder, чтобы разобрать или создать строку подключения и преобразовать её в ClickHouseTcpClientOptions.
В следующих разделах перечислены доступные настройки, их значения по умолчанию и описано, на что они влияют.

Настройки соединения

Значения свойств TimeSpan в строке подключения задаются в секундах.

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

Пул соединений

Одно соединение может выполнять только одну операцию за раз. Пул позволяет выполнять операции параллельно и повторно использовать соединения для разных операций.

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

Без TLS пароль и все данные запросов передаются по сети без шифрования. Вне локальной разработки оставляйте для TlsAllowInvalidCertificates значение false.
Для защищённого собственного протокола используется отдельная конечная точка сервера: переключиться на TLS в рамках уже установленного соединения нельзя. Клиент с TLS должен подключаться к защищённому порту собственного протокола; перехода на соединение без шифрования не происходит. ConfigureTls применяется последним и может переопределить проверку сертификата и имени хоста, поэтому тщательно проверяйте пользовательские функции обратного вызова.

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

Логгер клиента записывает события жизненного цикла и операций на стороне клиента. Пакеты серверного журнала вместо этого передаются в ClickHouseTcpQueryCallbacks.OnLog. Текст оператора может содержать конфиденциальные данные, поэтому по умолчанию он не включается в теги активности, а в отладочных журналах существенно обрезается.

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

ClickHouseTcpClientOptions.CustomSettings применяет настройки сервера ClickHouse к каждому запросу и каждой вставке. Значения ClickHouseTcpQueryOptions.Settings, заданные для конкретной операции, переопределяют одноимённые настройки на уровне клиента.
В строке подключения добавляйте к имени каждой настройки сервера префикс set_:
Не указывайте префикс set_ при добавлении настройки в CustomSettings или в Settings отдельной операции.

ClickHouseTcpQueryOptions

ClickHouseTcpQueryOptions задаёт настройки для отдельной операции. Все свойства необязательны.

ClickHouseTcpInsertOptions

ClickHouseTcpInsertOptions расширяет ClickHouseTcpQueryOptions настройками для вставки нативных блоков. Все свойства параметров запроса также доступны.

ClickHouseTcpClient

ClickHouseTcpClient — высокоуровневый API для собственного TCP-протокола. Он выполняет команды и скалярные запросы, передаёт результаты потоком в виде столбцовых блоков, строк или объектов POCO, вставляет данные по столбцам или строкам и открывает сеансы, закреплённые за соединением. Клиент потокобезопасен и управляет пулом соединений. Создайте по одному клиенту для каждой конечной точки, используйте его во всём приложении и освобождайте его ресурсы при остановке. Одновременно может выполняться до MaxPoolSize операций; остальные ожидают свободного соединения не дольше PoolTimeout.

Создание клиента

Создайте клиент, используя строку подключения или ClickHouseTcpClientOptions. Доступные параметры см. в разделе Конфигурация. Создание клиента с помощью строки подключения:
С использованием строго типизированных параметров:
Рекомендуется использовать асинхронное освобождение ресурсов. При освобождении клиент закрывает бездействующие соединения и ожидает завершения активных операций не дольше PoolTimeout, после чего прерывает их.

Внедрение зависимостей

AddClickHouseTcpDataSource регистрирует один источник данных типа singleton и предоставляет доступ к его общему клиенту через интерфейсы IClickHouseTcpClient и IClickHouseTcpOperations:
Провайдер сервисов владеет источником данных и пулом соединений. Не освобождайте ресурсы клиента, полученного через внедрение зависимостей; освобождайте ресурсы провайдера при остановке приложения. Если в параметрах не указан LoggerFactory, при регистрации используется ILoggerFactory контейнера. serviceKey позволяет зарегистрировать несколько независимых источников данных.

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

Для команд, которые не возвращают строк, например DDL-команд и мутаций, используйте ExecuteAsync:
Метод завершается после того, как сервер подтвердит получение оператора. Если оператор возвращает строки, ExecuteAsync считывает и отбрасывает их. Для INSERT INTO ... VALUES используйте InsertAsync или InsertRowsAsync, а SQL-оператор завершайте ключевым словом VALUES, не указывая значения непосредственно в запросе.

Чтение данных

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

Чтение скалярных значений

ExecuteScalarAsync возвращает значение из первого столбца первой строки как object:
Метод считывает ответ целиком и отбрасывает все значения, кроме первого. Пишите запрос, который возвращает одну строку, а не рассчитывайте, что этот метод досрочно прервёт чтение большого результата. Если первое значение равно NULL или запрос не возвращает ни одной строки, в обоих случаях результатом будет null.

Чтение строк

QueryAsync потоково выдаёт каждую строку как отдельный массив object[], значения в котором расположены в порядке столбцов результата:
Массив строки остаётся пригодным для использования после перехода перечислителя к следующему элементу. Этот вариант удобен, когда нет модели результата, но для каждой строки создаётся отдельный массив, а значения значимых типов упаковываются в объекты.

Чтение данных в объекты POCO

QueryAsync<T> сопоставляет столбцы с общедоступными свойствами, доступными для записи. При сопоставлении имён не учитываются регистр и символы подчёркивания. Чтобы явно задать имя, используйте ClickHouseTcpColumn, а чтобы исключить свойство — ClickHouseTcpNotMapped.
Сопоставление с POCO требует преобразований и выделения памяти под отдельный объект для каждой строки.

Чтение данных столбцовыми блоками

StreamAsync возвращает результат в нативном столбцовом формате как последовательность блоков Block. Это самый быстрый способ чтения данных.
Блоки передаются во временное пользование. Блок, его столбцы и диапазоны значений в них доступны только в текущей итерации. Не освобождайте и не сохраняйте их; данные, которые должны оставаться доступными после завершения итерации, необходимо явно скопировать в выделенную вами память.
Обязательно завершите перебор или освободите перечислитель, чтобы связанное с ним соединение вернулось в пул.
Block.Column<T> предоставляет доступ к значениям в типе CLR, сформированном декодером. Если требуется преобразование, используйте Block.ReadAs<T> или специализированный интерфейс, например IDateTimeColumn.

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

Нативные вставки передают данные в виде столбцовых блоков в двоичном формате. SQL должен содержать оператор INSERT INTO ... VALUES без списка значений в самом запросе. Чтобы свести к минимуму обработку на стороне клиента, используйте типизированные столбцы; если данные в приложении уже представлены построчно, используйте перегрузки для строк и объектов POCO.

Столбцовая вставка

InsertAsync принимает данные, уже сгруппированные по столбцам с заданными типами. Это самый быстрый способ вставки данных.
Столбцы сопоставляются со столбцами из списка в SQL-операторе по именам, а не по порядку аргументов; число строк во всех столбцах должно совпадать. Для столбцов таблицы, не указанных в операторе, сервер использует значения по умолчанию. Не изменяйте массивы, принадлежащие вызывающему коду, до завершения вставки. Этот API позволяет обойтись без преобразования строк в столбцы и упаковки значений в объектные типы, которые требуются при построчной вставке. Столбец, полученный из заимствованного блока результатов, также можно передать напрямую в InsertAsync, если вставка завершится до освобождения этого блока.

Вставка строк

InsertRowsAsync принимает строки в виде массивов object[]. Значения сопоставляются со столбцами из списка в SQL-запросе в порядке их следования:
Строки преобразуются в столбцы блок за блоком. Каждая строка должна содержать по одному значению для каждого столбца, указанного в операторе. Значения в пределах одного столбца должны иметь одинаковый тип CLR, если только целевой тип не Variant или Dynamic. Не изменяйте строки до завершения операции.

Вставка данных из POCO

Обобщённая перегрузка InsertRowsAsync<T> сопоставляет столбцы целевой таблицы с общедоступными свойствами, доступными для чтения, по тем же правилам именования и использования атрибутов, что и при чтении данных в POCO:
Каждому столбцу, указанному в операторе, должен соответствовать совместимый общедоступный геттер. Строки POCO преобразуются по одному блоку в формате передачи за раз, поэтому недопустимое значение в одном из последующих блоков может обнаружиться уже после отправки предыдущих блоков. ClickHouseTcpNotMapped исключает из сопоставления свойства, используемые только приложением, например ImportBatch.

Параметры SQL

Нативный клиент подставляет значения в плейсхолдеры {name:Type} нативного синтаксиса ClickHouse. Плейсхолдеры @name в стиле ADO.NET он не преобразует.
Тип каждого параметра должен быть указан либо в плейсхолдере SQL, либо в свойстве ClickHouseTcpParameter.ClickHouseType. Для имён таблиц и столбцов используйте тип Identifier. Если значение DateTime или DateTimeOffset обозначает конкретный момент времени, укажите часовой пояс, например {timestamp:DateTime('UTC')}; в противном случае часовой пояс сеанса сервера может повлиять на интерпретацию этого значения.

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

Каждой операции соответствует идентификатор запроса, который используется в системных таблицах ClickHouse, журналах клиента и спанах трассировки. Если QueryId имеет значение null или пуст, клиент генерирует новый GUID. Укажите его, если операцию необходимо коррелировать с идентификатором приложения:

Ход выполнения запроса и метаданные

Настройте ClickHouseTcpQueryOptions.Callbacks, чтобы получать метаданные вперемежку с данными ответа:
Значения прогресса отражают приращения, а не накопленные итоги. Обратные вызовы выполняются синхронно в потоке, который читает ответ, в порядке поступления пакетов; обработчики должны работать быстро и не генерировать исключений. Блоки, переданные в OnLog, OnProfileEvents, OnTotals и OnExtremes, доступны только на время вызова и не должны использоваться после завершения обратного вызова. ClickHouse не отправляет пакеты о ходе выполнения запроса для строк, загружаемых при вставке. Используйте OnBlockWritten, чтобы отслеживать блоки, отправленные клиентом; этот обратный вызов сообщает о ходе передачи, но не о том, что сервер закоммитил вставку.

Отмена

Каждая операция принимает CancellationToken:
Отмена или досрочное прекращение чтения потокового результата не позволяет безопасно использовать соединение повторно, поэтому клиент закрывает его, а не возвращает в пул. При этом клиентом по-прежнему можно пользоваться: для последующих операций он открывает новое соединение или использует другое существующее. Если сеанс закреплён за соединением, отмена может завершить этот сеанс, и его временные таблицы и настройки будут потеряны.

Информация о сервере и проверки его работоспособности

GetServerInfoAsync возвращает идентификатор сервера, его версию, часовой пояс и сведения о протоколе, согласованном при рукопожатии по нативному протоколу:
Поддержку возможностей на уровне двоичного представления данных определяйте по ProtocolRevision — ревизии, согласованной клиентом и сервером. Поддержку возможностей SQL, таких как типы данных и функции, определяйте по Version. Метод использует существующее соединение, если оно доступно, и открывает новое, если пул пуст. Используйте PingAsync для проверки готовности по собственному протоколу:
Успешный Ping подтверждает подключение по нативному протоколу и прохождение аутентификации. При этом он не проверяет доступ к таблице приложения.

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

Полные примеры, которые можно запустить и которые охватывают чтение данных, вставку, типы данных, сеансы, TLS, обсервабилити и обработку ошибок, см. в примерах использования нативного TCP в репозитории GitHub.

Сеансы

Обычные операции клиента используют любое свободное соединение из пула, поэтому состояние, связанное с соединением, не изолировано и может не сохраняться между вызовами. Соединения из одного пула могут вести к разным серверам. Откройте сеанс, если для работы с временными таблицами, выполнения операторов SET или последовательности других операций требуется одно и то же соединение.
IClickHouseTcpSession предоставляет те же API для выполнения запросов и команд, вставки данных, проверки доступности и получения информации о сервере, что и клиент. Сеанс не является потокобезопасным. Операции в нём выполняются последовательно: попытка начать новую операцию до завершения текущей будет отклонена. При потоковой обработке результата сеанс остаётся занятым, пока результат не будет полностью перебран или не будет освобождён его перечислитель. Каждый сеанс занимает один слот MaxPoolSize на протяжении всего времени своего существования. Клиент может продолжать использовать другие соединения из пула, но если в пуле всего одно соединение, другие операции клиента будут ожидать, пока сеанс не будет освобождён или не истечёт время ожидания PoolTimeout. Освободите сеанс до того, как освободите создавший его клиент. При освобождении сеанса закреплённое за ним соединение закрывается, а не возвращается в пул. Благодаря этому последующие операции не унаследуют временные таблицы или настройки сеанса. Сбой соединения или протокола, отмена операции либо не полностью обработанный поток результатов могут сделать сеанс непригодным для использования; если IsOpen принимает значение false, откройте новый сеанс.

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

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

Создайте по одному ClickHouseTcpClient или ClickHouseTcpDataSource для каждой уникальной комбинации конечной точки и конфигурации и используйте его в течение всего времени работы приложения. Клиент безопасен для использования из нескольких потоков и управляет собственным пулом соединений. Если создавать клиент для каждой операции, каждый раз будет создаваться новый пул, а повторное использование соединений станет невозможным. В приложении с внедрением зависимостей один раз зарегистрируйте AddClickHouseTcpDataSource и внедряйте IClickHouseTcpClient. Жизненным циклом клиента управляет контейнер зависимостей, поэтому компоненты, получающие клиент, не должны освобождать внедрённый экземпляр. Если внедрение зависимостей не используется, освобождайте клиент при остановке приложения с помощью await using. Каждое соединение по нативному протоколу выполняет только одну операцию за раз. Поэтому MaxPoolSize ограничивает и число открытых соединений, и число параллельных операций; дополнительные вызовы ожидают свободного соединения не дольше PoolTimeout. Выбирайте значение с учётом фактического параллелизма приложения, не превышая установленных на сервере ограничений на соединения и запросы. Увеличение этого значения не ускорит отдельный запрос. Учитывайте, что соединения из пула также используются в следующих случаях:
  • При потоковой обработке результата соединение остаётся занятым, пока не будет освобождён перечислитель.
  • Сеанс удерживает одно соединение на протяжении всего времени своего существования и выполняет только одну операцию за раз.
  • Если обработка результата прервана, операция отменена во время выполнения или произошёл сбой протокола либо соединения, соединение закрывается, а не используется повторно.
Своевременно освобождайте сеансы и всегда перебирайте асинхронные результаты до конца либо освобождайте их перечислитель. Используйте категорию логирования ClickHouse.Driver.Tcp.Pool для диагностики исчерпания пула, неожиданного закрытия соединений или частого повторного установления соединений.

Тайм-ауты и отмена

У TCP-клиента для разных этапов ожидания предусмотрены отдельные ограничения: Получение соединения, при котором сначала нужно дождаться свободного слота в пуле, а затем установить новое соединение, может занять до PoolTimeout + DialTimeout. Отсчёт ReadTimeout начинается заново при каждом чтении из транспорта, поэтому длительный запрос, в ходе которого продолжают поступать сведения о ходе выполнения или данные, может выполняться дольше этого времени. Установите значение, достаточное для допустимых периодов, когда сервер не передаёт данные; задавайте TimeSpan.Zero только в том случае, если длительность операции надёжно ограничена другим тайм-аутом. Если нужно ограничить время выполнения всей операции, используйте токен отмены:
Клиент пытается отправить пакет Cancel нативного протокола, если это безопасно, а затем закрывает соединение, ответ из которого не был полностью считан. Клиент из пула остаётся пригодным для последующих операций, но после отмены закреплённый сеанс может стать непригодным для использования. Результат отменённой вставки или вставки, завершившейся по тайм-ауту, может остаться неизвестным; руководствуйтесь рекомендациями по повторным попыткам вставки, а не повторяйте её вслепую.

Обработка даты и времени

Для временных меток предпочтительны DateTime('UTC') или DateTime64(S, 'UTC'), а в прикладном коде — DateTimeOffset или DateTime со значением Utc для Kind. Так момент времени задаётся однозначно и не зависит от часового пояса сервера или сеанса. При записи значений DateTime TCP-клиент применяет следующие правила: Значение Unspecified, попадающее в пропущенный при переходе на летнее время интервал, отклоняется: ему не соответствует ни один момент времени. Если при возврате на зимнее время показание часов повторяется, выбирается первый из двух моментов. Используйте DateTimeOffset, когда выбор необходимо задать явно. Для параметров SQL, обозначающих момент времени, указывайте часовой пояс в плейсхолдере:
При столбцовом чтении DateTime и DateTime64 по умолчанию возвращают исходные числовые значения в типах uint или long. Используйте Block.ReadAs<DateTimeOffset>, IDateTimeColumn.GetDateTimeOffset или соответствующее свойство POCO, если удобнее работать с календарной датой и временем. Если важна каждая цифра, сохраняйте исходное числовое значение: разрешение DateTimeOffset составляет 100 наносекунд, поэтому при чтении через него значений DateTime64 с масштабом 8 или 9 теряется точность на уровне долей тика. Используйте DateOnly для Date и Date32. Для значений Time и Time64, которые могут быть отрицательными или превышать 24 часа, используйте TimeSpan; TimeOnly подходит только тогда, когда значения столбца ограничены временем суток.

Потоковая обработка и время жизни результатов

Для обработки с высокой пропускной способностью используйте StreamAsync и типизированные столбцы. Это соответствует столбцовой структуре ClickHouse и позволяет избежать создания массивов для каждой строки, упаковки значений и выделения памяти под POCO. Используйте QueryAsync<T>, когда важнее получить модель приложения, владеющую своими данными, чем свести выделение памяти к минимуму, а нетипизированный QueryAsync — только если схема неизвестна на этапе компиляции. Блоки, возвращаемые StreamAsync, предоставляются без передачи владения. Блок, его столбцы, дочерние столбцы составных типов и все спаны доступны только в течение текущей итерации цикла. Обрабатывайте их на месте или копируйте значения, которые понадобятся после завершения итерации:
Не освобождайте возвращённый блок вручную. await foreach сам освободит перечислитель. Если нужна только часть результата, ограничьте выборку в SQL с помощью LIMIT и выбирайте только необходимые столбцы. Досрочное прекращение перебора отменяет получение оставшейся части ответа и не позволяет повторно использовать соединение.

Структура вставки и формирование батчей

Если данные уже организованы по столбцам, используйте InsertAsync с типизированными столбцами: этот метод сводит к минимуму преобразования на стороне клиента. Для объектов POCO лучше подходит InsertRowsAsync<T>. Строки в виде нетипизированных object[] дают наибольшую гибкость, но требуют выделения памяти под массивы и упаковки значимых типов. Передавайте достаточное количество строк за один вызов вставки вместо множества небольших вставок. В пределах одного вызова MaxRowsPerBlock разбивает данные на блоки нативного формата передачи. Значение по умолчанию — 50 000 строк — также ограничивает размер временных буферов столбцов при преобразовании строк и объектов POCO. Увеличение этого значения может повысить пропускную способность для строк с небольшим числом столбцов, но увеличит пиковое потребление памяти. Если задать null, вся вставка будет записана одним блоком, а все входные данные, организованные по строкам, будут преобразованы за один раз. MaxSendBufferBytes отдельно ограничивает объём закодированных данных, которые накапливаются в буфере перед отправкой через сокет. Это мягкое ограничение проверяется между столбцами, поэтому один широкий столбец может его превысить. Строки и объекты POCO преобразуются по одному блоку за раз. Поэтому недопустимое значение в одном из последующих блоков может обнаружиться уже после отправки предыдущих блоков. Если качество данных вызывает сомнения, перед вставкой проверьте допустимость NULL и типы CLR. Если возможна повторная отправка логического батча, задайте DeduplicationToken на основе данных.

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

В таблицах ниже отдельно указаны тип, возвращаемый при декодировании IColumn<T>, и дополнительные типы, доступные при преобразовании через Block.ReadAs<T>, сопоставлении с POCO через QueryAsync<T> или вставке. Проверить, поддерживается ли конкретное сопоставление, можно с помощью того же механизма разрешения типов, который используется при этих операциях:
Правила сопоставления для Nullable, Array, Tuple, Map и LowCardinality рекурсивно применяются к вложенным типам.

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

Default .NET type — это тип T, доступный через Block.Column<T> и IColumn<T>. Типы, указанные в столбце Also readable as, можно читать через Block.ReadAs<T> и свойства POCO.

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

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

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

ClickHouseTcpDecimal хранит мантиссу как целое число произвольной разрядности и масштаб, что позволяет сохранять значения, точность которых превышает возможности типа decimal.

Булевый тип

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

Если данные могут не соответствовать кодировке UTF-8, используйте IStringColumn.GetBytes(row) или считывайте их как byte[]. FixedString(N) сохраняет все N байтов, включая нулевые байты в конце.

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

По умолчанию значения читаются как целые числа, чтобы при чтении по столбцам сохранялось их точное представление в передаваемых данных. При преобразовании в календарные значения используется часовой пояс, указанный в типе, а если он не указан — часовой пояс сеанса. При чтении Time или Time64 как TimeOnly генерируется исключение, если значение отрицательное или составляет не менее 24 часов. Для работы с полным диапазоном значений ClickHouse используйте TimeSpan.

Типы Enum

IEnumColumn также позволяет получить объявленное соответствие меток их числовым значениям.

Другие скалярные типы

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

Кортежи могут содержать до семи элементов. Столбец Nested не зависит от числа полей; его удобнее всего читать через INestedColumn, который предоставляет доступ к отдельным столбцам с именами полей и общим смещениям строк без выделения памяти под записи, упакованные в объекты. При заданном в ClickHouse по умолчанию параметре flatten_nested = 1 вложенные поля вместо этого поступают как отдельные столбцы Array(T) с точкой в имени.

Типы Variant, Dynamic и JSON

Variant и Dynamic возвращают для каждой строки значение CLR выбранного типа или null. Их столбцовые представления предоставляют поток дискриминаторов и по одному дочернему столбцу с заданным типом для каждого типа, встречающегося во время выполнения. Это позволяет избежать упаковки значений при обработке данных по столбцам. TCP-клиент считывает JSON с помощью сериализации ClickHouse в формате String. По умолчанию клиент включает output_format_native_write_json_as_string = 1; если отключить этот параметр, чтение JSON не поддерживается. ClickHouse разбирает и нормализует JSON, поэтому возвращаемый текст может отличаться от вставленного порядком ключей, пробелами и форматированием чисел.

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

Geometry представляется интерфейсом IVariantColumn, объединяющим шесть геометрических типов.

Тип QBit

TCP-клиент поддерживает форму QBit(T, N) с двумя аргументами, без параметра stride. Структура с тремя аргументами и параметром stride не поддерживается.

Типы AggregateFunction

Столбец типа AggregateFunction содержит промежуточное состояние, зависящее от функции. Получите окончательный результат на сервере, например с помощью sumMerge(column), и считайте полученное значение.

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

Каждая запись указывает тип CLR одной строки в IColumn, переданном в InsertAsync. Соответствие типов точное: TCP-клиент не выполняет неявных преобразований по аналогии с Convert.

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

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

При записи в формате BFloat16 каждое значение float усекается до 16-битного представления с плавающей запятой (brain floating point).

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

При записи генерируется исключение OverflowException, если количество цифр мантиссы после масштабирования превышает заданную точность.

Булевый тип

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

FixedString(N) намеренно не принимает значения типа string: их необходимо явно закодировать и дополнить до нужной длины, чтобы двоичное представление каждого значения было однозначным.

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

Целочисленные значения без преобразования задаются в единицах измерения, указанных в таблице для чтения. DateTimeOffset сохраняет момент времени. Для DateTime учитывается значение Kind: значение Unspecified интерпретируется как местное время в часовом поясе столбца, а если для столбца часовой пояс не задан — в часовом поясе сеанса.

Типы Enum

Неизвестное имя элемента перечисления отклоняется до отправки данных.

Другие скалярные типы

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

Допустимые структуры определяются рекурсивно: Array(Nullable(DateTime)) принимает DateTime?[] для каждой строки, а Map(String, UInt32) — KeyValuePair<string, uint>[]. При прямой записи Nested используется плотное представление столбцов, полученное при предыдущем чтении. Для вставок, формируемых приложением, используйте представление flatten_nested = 1 по умолчанию и записывайте каждое поле Array(T) с именем, содержащим точку, в отдельный столбец.

Типы Variant, Dynamic и JSON

Для Variant тип значения во время выполнения должен однозначно соответствовать типу CLR по умолчанию для одного из вариантов. Дополнительные преобразования, например запись DateTime в вариант DateTime, внутри Variant не применяются; вместо них используйте исходный тип по умолчанию для соответствующего варианта. Dynamic поддерживает скалярные значения, а также значения массивов, отображений и кортежей, в том числе вложенные, если их типы поддерживаются.

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

Если столбец Geometry создаётся приложением, только Point и MultiPolygon имеют уникальные структуры CLR. У Ring и LineString структура совпадает, как и у Polygon и MultiLineString, поэтому клиент не может определить, какой из типов представляет значение object. Декодированный столбец Geometry сохраняет дискриминаторы, поэтому его можно повторно вставить без такой неоднозначности.

Тип QBit

Типы AggregateFunction

Сжатие

По умолчанию клиент использует сжатие LZ4 для записываемых блоков; ClickHouse также по умолчанию сжимает блоки ответа с помощью LZ4. Сжатие применяется к блокам результатов и блокам, передаваемым при вставке, а текст запроса, сведения о ходе его выполнения, исключения и другие пакеты протокола передаются без сжатия в обычном формате передачи данных. Чтобы выбрать кодек для клиента, укажите параметр Compression в строке соединения:
В эквивалентной конфигурации со строгой типизацией используется IClickHouseCompressor:
Установите Compressor = null, чтобы отключить сжатие. Пользовательский компрессор должен поддерживать фрейминг блоков нативного формата; компрессоры, поддерживающие только тела HTTP-сообщений, не принимаются при создании клиента.

Выбор кодека для ответа

Compressor определяет, как сжимаются блоки, записываемые клиентом. Пакет Query нативного протокола запрашивает у сервера сжатие, но не указывает кодек. Поэтому ClickHouse выбирает кодек для ответа согласно настройке network_compression_method. По умолчанию сервер использует LZ4. При необходимости задавайте кодек для ответа отдельно для каждой операции:
LZ4 обычно требует меньше ресурсов CPU и лучше всего подходит для быстрых сетей в качестве кодека по умолчанию. ZSTD обычно создаёт фреймы меньшего размера, но немного сильнее нагружает CPU. Отключить сжатие может быть полезно, если сервер находится на том же хосте или подключён по другому очень быстрому каналу. Прежде чем менять настройку по умолчанию, проведите измерения на репрезентативных данных.

Обработка ошибок

Ошибки ClickHouse или соединения по собственному протоколу представлены исключениями, унаследованными от ClickHouseTcpException, который, в свою очередь, наследуется от DbException: ClickHouseTcpServerException содержит RawCode — точный числовой код, присланный сервером, и Code — соответствующее значение ClickHouseErrorCode, если для этого кода есть имя в драйвере. Также в нём доступны имя исключения сервера в Name, трассировка стека в ServerStackTrace и вложенные исключения сервера в InnerException.
Коды, отсутствующие в ClickHouseErrorCode, имеют значение Code == ClickHouseErrorCode.Unknown; проверяйте RawCode, поскольку перечисление может содержать не все коды ошибок ClickHouse. Ошибки проверки аргументов и жизненного цикла объектов по-прежнему представлены стандартными для .NET типами исключений. При отмене операции возникает OperationCanceledException. При DialTimeout и ReadTimeout возникает ClickHouseTcpConnectionException с вложенным исключением TimeoutException, а соединение удаляется из пула. При PoolTimeout возникает обычное исключение TimeoutException, а не ClickHouseTcpException: сбоя соединения не было. Чтобы устранить проблему, сократите число параллельных операций, увеличьте пул или завершите перебор результатов.

Повторное выполнение операций

Если во время вставки произошёл сбой соединения или истёк тайм-аут, результат операции остаётся неизвестным: сервер мог записать строки до того, как клиент потерял соединение и не получил ответ. Перед повторной попыткой задайте для логического батча постоянное значение DeduplicationToken:
При повторных попытках обработки того же батча используйте прежний токен, а для новых данных — другой. Остальные операции повторяйте только в том случае, если оператор безопасно выполнять повторно, а конкретный сбой носит временный характер; в большинстве случаев ошибки сервера требуют не повторной попытки, а изменения запроса или конфигурации.

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

TCP-клиент интегрируется с Microsoft.Extensions.Logging. Логирование необязательно: без LoggerFactory клиент не создаёт логгеры и не форматирует сообщения лога.
Если клиент зарегистрирован через расширения для внедрения зависимостей, он использует ILoggerFactory из контейнера, если фабрика логирования уже не указана в переданных параметрах.

Категории логирования

Настройте категории с помощью стандартных параметров логирования .NET:
Сообщение о начале операции записывается с уровнем Debug и содержит не более StatementMaxLength символов SQL. По умолчанию — пять символов. Установите значение не выше нуля, чтобы не записывать текст оператора в журналы, а увеличивайте его только после проверки параметров и литералов на наличие конфиденциальных данных.

Сообщения лога сервера

Интеграция логирования передаёт сведения о работе клиента. Пакеты журнала запросов самого ClickHouse передаются отдельно в ClickHouseTcpQueryCallbacks.OnLog. Чтобы включить их, используйте настройку сервера send_logs_level:
Блок журнала предоставляется временно и освобождается после завершения обратного вызова. Обрабатывайте его синхронно или копируйте значения, которые нужно сохранить. Обратные вызовы должны выполняться быстро и не должны генерировать исключения: исключение в обратном вызове прерывает операцию и разрывает соответствующее соединение.

OpenTelemetry

TCP-клиент создаёт спаны распределённой трассировки с помощью API System.Diagnostics.Activity в .NET. Подпишитесь на источник активности TCP-клиента с помощью провайдера трассировки OpenTelemetry:
Для консольного приложения или настройки вручную:
Имя источника событий — ClickHouse.Driver.Tcp. Оно отличается от имени источника HTTP-клиента, поэтому события для каждого из этих транспортов можно собирать независимо. Если ни один обработчик не подписан, операции не создают событий.

Спаны и атрибуты

Клиент создает спаны для SQL-операций, проверок доступности и новых соединений. Спан SQL-операции получает имя по первому ключевому слову оператора, например SELECT или INSERT; если его не удается распознать, используется query. Спан connect охватывает установление соединения через сокет, согласование TLS и рукопожатие по нативному протоколу. Спаны SQL-операций также включают время ожидания соединения из пула. Счетчики присутствуют, только если у сервера или клиента есть значимые значения для передачи. Например, при вставке с загрузкой блоков указывается количество строк, отправленных клиентом, но данные о прочитанных строках от сервера не поступают. Спаны неудачных операций также содержат событие exception с типом исключения, сообщением и трассировкой стека.

Текст SQL-запросов и конфиденциальные данные

По умолчанию текст SQL-запросов не включается в спаны. Включите его передачу явно и задайте подходящий лимит:
StatementMaxLength ограничивает длину атрибута db.query.text и SQL-текста в отладочных журналах. Если значение равно нулю или меньше, SQL-текст не включается ни туда, ни туда. Также проверяйте экспортируемые сообщения об исключениях и трассировки стека: ошибки сервера могут содержать подробности запросов.

Передача контекста трассировки

Если Activity.Current использует формат идентификаторов W3C, клиент записывает идентификатор трассировки, идентификатор спана, флаги и состояние трассировки в пакет Query нативного протокола. После этого ClickHouse может создавать серверные спаны в той же распределённой трассировке. Сбор серверных спанов и сэмплирование настраиваются на стороне ClickHouse; например, вероятность сэмплирования можно задать для каждого запроса:
Передаваемый Query id можно использовать вместе с идентификатором трассировки, чтобы коррелировать клиентский спан с system.query_log и, если ведение журнала спанов включено, с таблицей ClickHouse system.opentelemetry_span_log.

Ограничения

TCP-клиент пока экспериментальный и поддерживает не все возможности HTTP-клиента.

Возможности, доступные только через HTTP

TCP API не является провайдером ADO.NET и не может использоваться с ORM. Он также не поддерживает ввод и вывод в форматах CSV, JSONEachRow, Parquet или в виде сырого потока; аутентификацию по схеме bearer и пользовательские HTTP-заголовки; пользовательские правила определения типов параметров, форматирования и преобразования при чтении; указание базы данных или роли для каждого запроса. Если приложению нужна хотя бы одна из этих возможностей, используйте HTTP-клиент.

Параметры SQL

Поддерживаются только плейсхолдеры ClickHouse формата {name:Type}; TCP-клиент не преобразует плейсхолдеры ADO.NET вида @name. В версиях ClickHouse с 25.8 по 26.6 имена параметров, совпадающие с именами настроек сервера, например limit или offset, могут быть приняты за настройки и отклонены. Если необходимо поддерживать эти версии, переименуйте такие параметры, например в row_limit.

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

Полное соответствие типов описано в разделе Поддерживаемые типы данных. У текущего нативного кодека есть следующие ограничения:
  • Tuple может содержать не более семи элементов.
  • Промежуточные состояния AggregateFunction(function, ...) не поддерживаются. Завершайте их обработку на сервере, например с помощью sumMerge(column), и считывайте готовый результат.
  • Поддерживается только структура QBit(T, N) с двумя аргументами, без разбиения измерений на группы.
  • Для JSON используется сериализация ClickHouse в тип String, поэтому необходима настройка output_format_native_write_json_as_string = 1, которую клиент запрашивает по умолчанию.
  • При вставке данных, сформированных приложением, для Nested(...) недоступно построчное представление, если flatten_nested = 0. Вместо него используйте развёрнутые столбцы Array(T) с именами через точку, применяемые по умолчанию.
  • Для столбца Geometry, сформированного приложением, нельзя автоматически определить тип среди вариантов с одинаковой структурой CLR, например Ring и LineString. Вставляйте данные с конкретным геометрическим типом либо повторно вставляйте декодированный столбец Geometry, сохранивший свои дискриминаторы.

Автоматические повторные попытки

Клиент не повторяет операции автоматически после сбоя. Повторяйте только те операции, которые можно безопасно выполнить ещё раз. Если результат вставки неизвестен, перед повторной попыткой задайте для неё неизменный DeduplicationToken. Подробнее см. в разделе «Повторные попытки выполнения операций».
Последнее изменение 14 октября 2026 г.