Skip to main content

Estado experimental

La API del Client TCP nativo es experimental. Su interfaz pública puede cambiar en una versión futura.
Las interfaces del Client principal, del origen de datos, de sesión y de operaciones, así como las extensiones de inyección de dependencias, generan la advertencia del compilador CHTCP0001. Para reconocerla mientras evalúa la API, añada CHTCP0001 a NoWarn en el archivo de su proyecto:
Suprima la advertencia solo si acepta que el código que utiliza la API podría necesitar cambios en una futura versión.

Versiones de .NET compatibles

El Client TCP nativo es compatible con las siguientes versiones de .NET:
  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

Versiones de ClickHouse compatibles

El Client TCP es oficialmente compatible con las 2 últimas versiones LTS, así como con las 3 últimas versiones publicadas del servidor de ClickHouse.

Instalación

El Client TCP nativo se incluye en el paquete ClickHouse.Driver. Instálelo desde NuGet:
O bien, mediante el Administrador de paquetes NuGet:

Inicio rápido

Migración desde el Client HTTP

La API TCP es independiente de IClickHouseClient; no es una opción de transporte para ClickHouseClient ni para ClickHouseConnection. Puede reutilizar la mayor parte del SQL, pero debe crear un ClickHouseTcpClient y migrar cada operación a su equivalente para TCP.

Métodos equivalentes

Por ejemplo, un bucle de lectura con el Client HTTP:
se convierte en un flujo asíncrono de filas sobre TCP:

Opciones y parámetros

Las clases HTTP QueryOptions y ClickHouseParameterCollection no son intercambiables con sus equivalentes para TCP. Mueva los valores a ClickHouseTcpQueryOptions, donde los ajustes se expresan como cadenas y los parámetros forman parte del objeto de opciones:
El Client TCP admite los marcadores de posición nativos de ClickHouse {name:Type}, pero no reescribe los marcadores de posición @name al estilo de ADO.NET. Tampoco admite, por consulta, base de datos, rol, token Bearer, encabezado personalizado, resolvedor de parámetros o formateador, ni convertidor de lectura. Consulte limitaciones para conocer las funcionalidades que siguen estando disponibles solo a través de HTTP.

Duración de la conexión y de los resultados

Ambos Clients de alto nivel son seguros para subprocesos y deben reutilizarse. El Client TCP gestiona conexiones nativas persistentes en lugar de conexiones HTTP, implementa IAsyncDisposable y, por lo general, debe liberarse de forma asíncrona al cerrarse la aplicación. Las filas de QueryAsync pertenecen al llamador. Los bloques de StreamAsync y sus columnas son prestados y solo son válidos durante la iteración actual; copie los valores que deban conservarse más allá de ella. Finalice o libere siempre una enumeración para que su conexión vuelva al grupo. Use OpenSessionAsync cuando varias operaciones deban ejecutarse en la misma conexión y conservar el estado de la sesión. Consulte el ejemplo completo de migración de HTTP a TCP, en el que se usan ambos Clients sobre la misma tabla.

Configuración

Hay dos formas de configurar un Client TCP nativo:
  • Cadena de conexión: pares clave/valor separados por punto y coma que especifican el endpoint del servidor, las credenciales de autenticación y otras opciones de conexión.
  • Objeto ClickHouseTcpClientOptions: un objeto de configuración inmutable y fuertemente tipado que se define en el código.
Pase cualquiera de las dos al constructor de ClickHouseTcpClient. Utilice ClickHouseTcpConnectionStringBuilder para analizar o generar una cadena de conexión y convertirla en ClickHouseTcpClientOptions.
En las siguientes secciones se enumeran las opciones de configuración disponibles, sus valores predeterminados y sus efectos.

Configuración de la conexión

Los valores de las propiedades TimeSpan en la cadena de conexión se expresan en segundos.

Formato de datos y serialización

Agrupación de conexiones

Cada conexión ejecuta una sola operación a la vez. El grupo permite ejecutar operaciones simultáneamente y reutilizar las conexiones entre ellas.

Seguridad

Sin TLS, la contraseña y todos los datos de las consultas viajan por la red sin cifrar. Mantenga TlsAllowInvalidCertificates en false fuera del desarrollo local.
El protocolo nativo seguro es un endpoint independiente del servidor, no una actualización dentro de la misma conexión. Un Client TLS debe conectarse a un puerto nativo seguro; nunca recurre a una conexión sin cifrar. ConfigureTls se aplica en último lugar y puede sobrescribir la validación del certificado y del nombre del host, así que revise con atención los callbacks personalizados.

Registro y depuración

El logger del Client registra los eventos del ciclo de vida y de las operaciones del lado del Client. En cambio, los paquetes de log del servidor se envían a ClickHouseTcpQueryCallbacks.OnLog. El texto de las sentencias puede contener datos sensibles, por lo que, de forma predeterminada, se excluye de las etiquetas de actividad y se trunca considerablemente en los logs de depuración.

Ajustes personalizados

ClickHouseTcpClientOptions.CustomSettings aplica ajustes del servidor de ClickHouse a todas las consultas y operaciones de inserción. Los valores de ClickHouseTcpQueryOptions.Settings definidos para cada operación prevalecen sobre los valores del Client con el mismo nombre.
En una cadena de conexión, anteponga el prefijo set_ a cada configuración del servidor:
No incluya el prefijo set_ al añadir una configuración a CustomSettings o a los Settings de cada operación.

ClickHouseTcpQueryOptions

ClickHouseTcpQueryOptions permite configurar cada operación por separado. Todas las propiedades son opcionales.

ClickHouseTcpInsertOptions

ClickHouseTcpInsertOptions extiende ClickHouseTcpQueryOptions con opciones de configuración para inserciones de bloques nativos. También están disponibles todas las propiedades de las opciones de consulta.

ClickHouseTcpClient

ClickHouseTcpClient es la API de alto nivel para el protocolo TCP nativo. Ejecuta sentencias y consultas escalares, transmite los resultados como bloques columnares, filas u objetos POCO, inserta datos columnares o datos organizados en filas y abre sesiones fijadas a una conexión. El Client es seguro para subprocesos y mantiene un grupo de conexiones. Cree un Client por cada punto de conexión, compártalo en toda la aplicación y libere sus recursos al cerrarla. Pueden ejecutarse operaciones simultáneas hasta el límite de MaxPoolSize; las demás esperan hasta PoolTimeout para obtener una conexión.

Creación de un Client

Cree un Client mediante una cadena de conexión o ClickHouseTcpClientOptions. Consulte Configuración para ver las opciones disponibles. Con una cadena de conexión:
Uso de opciones fuertemente tipadas:
Prefiera liberar los recursos de forma asíncrona. Al liberar los recursos del Client, se cierran las conexiones inactivas y se espera hasta PoolTimeout a que terminen las operaciones activas antes de cancelarlas.

Inyección de dependencias

AddClickHouseTcpDataSource registra una única instancia de origen de datos y expone su Client compartido como IClickHouseTcpClient e IClickHouseTcpOperations:
El proveedor de servicios gestiona el origen de datos y el grupo de conexiones. No libere los recursos de un Client inyectado; libere los del proveedor al cerrar la aplicación. Si las opciones no especifican un LoggerFactory, el registro utiliza el ILoggerFactory del contenedor. Con un serviceKey se pueden registrar varios orígenes de datos independientes.

Ejecutar consultas

Use ExecuteAsync para las sentencias que no devuelven filas, como las sentencias DDL y las mutaciones:
El método finaliza cuando el servidor confirma la sentencia. Si una sentencia devuelve filas, ExecuteAsync las lee y las descarta. Para INSERT INTO ... VALUES, utilice InsertAsync o InsertRowsAsync y termine la sentencia SQL en VALUES, sin incluir valores en línea.

Lectura de datos

Elija la API de lectura según el formato de datos que necesite la aplicación:

Lecturas escalares

ExecuteScalarAsync devuelve la primera columna de la primera fila como object:
El método lee la respuesta completa y descarta todos los valores salvo el primero. Escriba una consulta que devuelva una sola fila, en lugar de usar el método para detener prematuramente la lectura de un resultado extenso. Tanto si el primer valor es NULL como si la consulta no devuelve ninguna fila, el resultado es null.

Lectura por filas

QueryAsync transmite cada fila como un object[] independiente, con los valores ordenados según las columnas del resultado:
El array de la fila sigue siendo válido al avanzar la enumeración. Este nivel resulta útil cuando no existe un modelo de resultados, pero asigna un array por fila y convierte los tipos de valor en objetos.

Lectura de datos en objetos POCO

QueryAsync<T> asigna las columnas a propiedades públicas con un método de asignación. La correspondencia entre nombres no distingue mayúsculas de minúsculas ni tiene en cuenta los guiones bajos; usa ClickHouseTcpColumn para especificar un nombre y ClickHouseTcpNotMapped para excluir una propiedad.
El mapeo de objetos POCO realiza conversiones y asigna memoria para un objeto por fila.

Lectura de bloques columnares

StreamAsync expone el resultado columnar nativo como una secuencia de valores Block. Es la forma más rápida de leer datos.
Los bloques se toman prestados. Un bloque, sus columnas y sus segmentos de valores solo son válidos durante la iteración actual. No los libere ni los conserve; cualquier dato que deba seguir disponible después de ejecutar el cuerpo del bucle debe copiarse explícitamente a una región de memoria bajo su control.
Complete siempre la enumeración o libere el enumerador para que su conexión vuelva al grupo.
Block.Column<T> permite acceder al tipo CLR que produce el decodificador. Utilice Block.ReadAs<T> o una interfaz especializada como IDateTimeColumn cuando sea necesario realizar una conversión.

Inserción de datos

Las inserciones nativas envían los datos en bloques columnares en formato wire. La sentencia SQL debe ser INSERT INTO ... VALUES sin una lista de valores en la propia sentencia. Use columnas con tipos definidos para reducir al mínimo el procesamiento en el cliente, o las sobrecargas para filas y POCO si la aplicación ya dispone de datos organizados por filas.

Inserciones columnares

InsertAsync acepta datos ya agrupados en columnas con tipos definidos. Es la forma más rápida de insertar datos.
Las columnas se corresponden con las de la lista de columnas SQL por nombre, no por el orden de los argumentos, y deben tener el mismo número de filas. El servidor asigna los valores predeterminados a las columnas de la tabla omitidas en la sentencia. No modifique los arrays del llamador hasta que finalice la inserción. Esta API evita la conversión de filas a columnas y el boxing de tipos de valor necesarios para las inserciones por filas. Una columna leída de un bloque de resultados prestado también puede pasarse directamente a InsertAsync, siempre que la inserción finalice antes de que se libere ese bloque.

Inserción de filas

InsertRowsAsync acepta filas de tipo object[]. Los valores se asignan a las columnas de la lista SQL según su posición:
Las filas se convierten en columnas bloque a bloque. Cada fila debe contener un valor por cada columna especificada en la sentencia, y los valores de una misma columna deben tener el mismo tipo CLR, salvo si el destino es Variant o Dynamic. No modifique las filas hasta que termine la operación.

Inserciones con objetos POCO

La sobrecarga genérica InsertRowsAsync<T> asocia las columnas de destino con propiedades públicas de lectura según las mismas reglas de nomenclatura y atributos que se aplican al leer objetos POCO:
Cada columna indicada en la sentencia debe corresponder a un getter público compatible. Las filas POCO se convierten bloque wire por bloque wire, por lo que un valor no válido en un bloque posterior puede detectarse cuando los bloques anteriores ya se han enviado. ClickHouseTcpNotMapped evita que las propiedades de uso exclusivo de la aplicación, como ImportBatch, se incluyan en la correspondencia.

Parámetros SQL

El Client nativo asocia los valores con los marcadores de posición {name:Type} propios de ClickHouse. No reescribe los marcadores de posición @name de ADO.NET.
Cada parámetro debe tener un tipo, especificado en su marcador de posición SQL o en la propiedad ClickHouseTcpParameter.ClickHouseType. Utilice el tipo Identifier para los nombres de tablas y columnas. Si un DateTime o DateTimeOffset representa un instante concreto, incluya una zona horaria como {timestamp:DateTime('UTC')}; de lo contrario, la zona horaria de la sesión del servidor podría cambiar su significado.

ID de consulta

Cada operación tiene un ID de consulta que utilizan las tablas del sistema de ClickHouse, los logs del Client y los spans de traza. Si QueryId es nulo o está vacío, el Client genera un nuevo GUID. Establezca su valor cuando sea necesario correlacionar la operación con un identificador de la aplicación:

Progreso de la consulta y metadata

Configure ClickHouseTcpQueryOptions.Callbacks para recibir metadata intercalada con la respuesta:
Los valores de progreso indican incrementos, no totales acumulados. Las funciones de callback se ejecutan de forma síncrona en el thread que lee la respuesta, en el orden en que llegan los paquetes; deben ejecutarse rápidamente y no lanzar excepciones. Los bloques que reciben OnLog, OnProfileEvents, OnTotals y OnExtremes son prestados y no deben utilizarse fuera de la función de callback. ClickHouse no envía paquetes de progreso de la consulta para las filas cargadas mediante una inserción. Use OnBlockWritten para observar los bloques enviados por el Client; esta función informa del progreso de la transmisión, pero no confirma que el servidor haya completado el commit de la inserción.

Cancelación

Todas las operaciones aceptan un CancellationToken:
Cancelar o abandonar un resultado transmitido progresivamente impide reutilizar de forma segura su conexión, por lo que el Client la cierra en lugar de devolverla al grupo. El Client sigue funcionando y abre o reutiliza otra conexión para operaciones posteriores. Si la sesión está vinculada a una conexión fija, cancelarla puede finalizar la sesión y provocar la pérdida de sus tablas temporales y su configuración.

Información del servidor y comprobaciones de estado

GetServerInfoAsync devuelve la identity del servidor, su versión, la zona horaria y la información del protocolo negociado durante el handshake nativo:
Habilite las funcionalidades a nivel de wire según ProtocolRevision, la revisión negociada entre el Client y el servidor. Habilite las funcionalidades de SQL, como los tipos de datos y las funciones, según Version. El método reutiliza una conexión si hay alguna disponible y abre una nueva cuando el grupo está vacío. Use PingAsync para comprobar si el servicio está listo mediante el protocolo nativo:
Un ping satisfactorio confirma la conectividad nativa y la autenticación, pero no el acceso a una tabla de la aplicación.

Más ejemplos

Para consultar ejemplos completos y ejecutables sobre lectura e inserción de datos, tipos, sesiones, TLS, observabilidad y gestión de errores, consulte los ejemplos de TCP nativo en el repositorio de GitHub.

Sesiones

Las operaciones habituales del Client utilizan cualquier conexión disponible del grupo, por lo que el estado asociado a una conexión no queda aislado ni se garantiza que persista entre llamadas. Las conexiones del grupo pueden estar vinculadas a distintos servidores. Abra una sesión cuando las tablas temporales, las sentencias SET u otra secuencia de operaciones deban utilizar la misma conexión.
IClickHouseTcpSession ofrece las mismas API de consulta, ejecución, inserción, ping e información del servidor que el Client. La sesión no es segura para su uso simultáneo desde varios threads. Las operaciones deben ejecutarse de forma secuencial: si se inicia otra antes de que termine la actual, se rechaza. Un resultado transmitido progresivamente mantiene ocupada la sesión hasta que se recorren todos sus elementos o se libera su enumerador. Cada sesión ocupa un slot de MaxPoolSize durante toda su duración. El Client puede seguir utilizando otras conexiones del grupo, pero, si el grupo solo tiene una conexión, las demás operaciones del Client deben esperar hasta que se libere la sesión o venza PoolTimeout. Libere la sesión antes de liberar su Client padre. Al hacerlo, la conexión reservada se cierra en lugar de devolverse al grupo. Así se evita que los llamadores posteriores hereden sus tablas temporales o su configuración. Un fallo de conexión o de protocolo, una cancelación o un flujo de resultados incompleto pueden dejar la sesión inutilizable; cuando IsOpen pase a ser false, abra una sesión nueva.

Buenas prácticas

Duración del Client y agrupación de conexiones

Cree un ClickHouseTcpClient o ClickHouseTcpDataSource para cada combinación de endpoint y configuración, y reutilícelo durante toda la vida de la aplicación. El Client es seguro para subprocesos y gestiona su propio grupo de conexiones; crear uno por operación genera un grupo nuevo cada vez e impide reutilizar las conexiones. En una aplicación con inyección de dependencias, registre AddClickHouseTcpDataSource una sola vez e inyecte IClickHouseTcpClient. El proveedor de servicios es propietario del Client, por lo que quienes usen la instancia inyectada no deben liberarla. Fuera de la inyección de dependencias, libere el Client al detener la aplicación mediante await using. Cada conexión nativa procesa una sola operación a la vez. Por tanto, MaxPoolSize limita tanto las conexiones abiertas como las operaciones simultáneas; los llamadores adicionales esperan a que quede un cupo libre durante un máximo de PoolTimeout. Elija un valor que permita atender la concurrencia real de la aplicación sin superar los límites de conexiones y consultas del lado del servidor. Aumentarlo no acelera las consultas individuales. Tenga en cuenta qué operaciones ocupan conexiones del grupo:
  • Un resultado transmitido progresivamente mantiene ocupada su conexión hasta que se libera su enumerador.
  • Una sesión mantiene ocupada una conexión durante toda su duración y solo admite una operación a la vez.
  • Si se abandona un resultado, se cancela una operación en curso o falla el protocolo o la conexión, esta se cierra en lugar de reutilizarse.
Libere las sesiones en cuanto deje de necesitarlas y recorra siempre los resultados asíncronos hasta el final o libere su enumerador. Use la categoría de registro ClickHouse.Driver.Tcp.Pool para diagnosticar el agotamiento del grupo, el descarte inesperado de conexiones o los nuevos intentos de conexión frecuentes.

Tiempos de espera y cancelación

El cliente TCP establece límites distintos para cada tipo de espera: Si para obtener una conexión primero hay que esperar a que haya un espacio libre en el grupo y después abrir una conexión nueva, el proceso puede tardar hasta PoolTimeout + DialTimeout. ReadTimeout se reinicia con cada lectura del transporte, por lo que una consulta larga que sigue enviando información de progreso o datos puede durar más que ese valor. Configure un valor lo bastante alto para permitir períodos normales de inactividad del servidor; use TimeSpan.Zero solo si otro tiempo de espera limita de forma fiable la operación. Use un token de cancelación cuando necesite un tiempo de espera para toda la operación:
El Client intenta enviar un paquete Cancel del protocolo nativo cuando puede hacerlo de forma segura y, después, cierra la conexión si no se ha consumido por completo la respuesta. Las operaciones posteriores con el Client del grupo siguen funcionando, pero la cancelación puede dejar inutilizable una sesión anclada. El resultado de una inserción cancelada o cuyo tiempo de espera se haya agotado puede ser incierto; siga las recomendaciones para reintentar inserciones en lugar de repetirla sin más.

Manejo de fechas y horas

Prefiera DateTime('UTC') o DateTime64(S, 'UTC') para las marcas de tiempo y use DateTimeOffset o un DateTime cuyo Kind sea Utc en el código de la aplicación. Así queda claro qué instante se representa y se evita depender de la zona horaria del servidor o de la sesión. El Client TCP aplica estas reglas al escribir valores DateTime: Se rechaza una hora no especificada que no existe debido al cambio al horario de verano, ya que no corresponde a ningún instante. Si una hora es ambigua durante el cambio al horario de invierno, se elige la primera aparición. Use DateTimeOffset cuando sea necesario indicar explícitamente cuál de las dos corresponde. Para los parámetros SQL que representan un instante, incluya la zona horaria en el marcador de posición:
En las lecturas columnares, DateTime y DateTime64 exponen de forma predeterminada sus recuentos sin procesar como uint o long. Use Block.ReadAs<DateTimeOffset>, IDateTimeColumn.GetDateTimeOffset o una propiedad POCO del tipo correspondiente si le resulta más cómodo trabajar con un valor de fecha y hora. Conserve el recuento sin procesar cuando cada dígito sea importante: DateTimeOffset tiene una resolución de 100 nanosegundos, por lo que leer DateTime64 con una escala de 8 o 9 mediante este tipo hace que se pierda la precisión inferior a un tick. Use DateOnly para Date y Date32. Use TimeSpan para los valores Time y Time64 que puedan ser negativos o superar las 24 horas; TimeOnly solo es adecuado cuando la columna se limita a una hora del día.

Procesamiento en streaming y duración de los resultados

Use StreamAsync y columnas tipadas para procesar datos con un caudal elevado. Esto se ajusta a la disposición columnar de ClickHouse y evita los arrays por fila, el boxing y las asignaciones de memoria para objetos POCO. Use QueryAsync<T> cuando sea más importante disponer de un modelo de aplicación propio que minimizar las asignaciones, y use la versión sin tipos de QueryAsync solo cuando el esquema no se conozca en tiempo de compilación. Los bloques devueltos por StreamAsync son prestados. El bloque, sus columnas, las columnas hijas de estructuras compuestas y todos los spans solo son válidos durante la iteración actual del bucle. Procéselos directamente o copie los valores que deban seguir disponibles después de esa iteración:
No libere usted mismo un bloque obtenido durante la iteración. Deje que await foreach libere el enumerador. Si solo necesita una parte del resultado, especifíquelo en SQL con LIMIT y seleccione únicamente las columnas necesarias. Salir de la iteración antes de tiempo cancela el resto de la respuesta e impide reutilizar esa conexión.

Estructura de las inserciones y procesamiento por lotes

Prefiera InsertAsync con columnas de tipos definidos cuando los datos ya estén organizados por columnas. Es la opción que requiere menos conversiones en el Client. InsertRowsAsync<T> es la opción natural para datos POCO; las filas object[] sin tipos definidos ofrecen más flexibilidad, pero requieren asignar arrays y convertir los tipos de valor en objetos. Envíe una cantidad considerable de filas en cada llamada de inserción, en lugar de realizar muchas inserciones pequeñas. En cada llamada, MaxRowsPerBlock divide los datos en bloques wire nativos. Su valor predeterminado de 50 000 filas también limita el tamaño de los búferes temporales de columnas utilizados al convertir entradas organizadas por filas y datos POCO. Aumentarlo puede mejorar el caudal cuando las filas tienen pocas columnas, pero incrementa el consumo máximo de memoria; establecerlo en null escribe toda la inserción en un solo bloque y convierte a la vez todas las entradas organizadas por filas de ese bloque. MaxSendBufferBytes limita por separado la cantidad de datos codificados que se almacenan en el búfer antes de su vaciado al socket. Es un límite flexible que se comprueba entre columnas, por lo que una sola columna ancha puede superarlo. Las entradas organizadas por filas y los datos POCO se convierten bloque a bloque. Por ello, un valor incorrecto en un bloque posterior puede detectarse cuando ya se han enviado los bloques anteriores. Valide la admisibilidad de valores nulos y los tipos CLR antes de la inserción si no tiene certeza de la calidad de los datos, y establezca un DeduplicationToken derivado de los datos siempre que pueda reintentarse el lote lógico.

Tipos de datos compatibles

Las tablas siguientes distinguen entre el tipo que devuelve IColumn<T> tras la decodificación y los tipos adicionales disponibles mediante la conversión con Block.ReadAs<T>, la correspondencia con POCO de QueryAsync<T> o las inserciones. Puede consultar al mismo resolvedor que utilizan esas operaciones si admite una correspondencia concreta:
Las correspondencias de Nullable, Array, Tuple, Map y LowCardinality se aplican de forma recursiva a sus tipos internos.

Correspondencia de tipos: lectura de ClickHouse

Default .NET type es el tipo T que exponen Block.Column<T> e IColumn<T>. Los tipos de la columna Also readable as se pueden leer mediante Block.ReadAs<T> y las propiedades de los objetos POCO.

Tipos de enteros

Tipos de coma flotante

Tipos decimales

ClickHouseTcpDecimal almacena una mantisa entera de tamaño arbitrario y una escala, lo que permite conservar valores cuya precisión supera la capacidad de decimal.

Tipo booleano

Tipos String

Use IStringColumn.GetBytes(row) o lea los datos como byte[] si es posible que no sean UTF-8 válido. FixedString(N) conserva los N bytes, incluidos los bytes nulos finales.

Tipos de fecha y hora

La representación como entero sin procesar es la predeterminada para que las lecturas columnares conserven el valor exacto en formato wire. Las conversiones a fecha y hora usan la zona horaria declarada por el tipo o, si este no declara ninguna, la zona horaria de la sesión. Leer Time o Time64 como TimeOnly genera una excepción si el valor es negativo o es igual o superior a 24 horas; use TimeSpan para abarcar todo el rango de ClickHouse.

Tipos enumerados

IEnumColumn también expone la correspondencia declarada entre las etiquetas y sus valores ordinales.

Otros tipos escalares

Tipos compuestos

Las tuplas pueden contener hasta siete elementos. Una columna Nested admite cualquier aridad y conviene leerla mediante INestedColumn, que permite acceder a sus columnas de campos planos con nombre y a los desplazamientos de fila compartidos sin tener que crear registros empaquetados como objetos. Con la configuración predeterminada de ClickHouse, flatten_nested = 1, los campos anidados llegan como columnas Array(T) independientes cuyos nombres contienen puntos.

Tipos Variant, Dynamic y JSON

Variant y Dynamic devuelven el valor CLR del tipo seleccionado en cada fila, o null. Sus vistas columnares exponen el flujo de discriminadores y una columna secundaria tipada por cada tipo en tiempo de ejecución, lo que evita el empaquetado al procesar los datos por columnas. El client TCP lee JSON mediante la serialización de ClickHouse para String. El client activa output_format_native_write_json_as_string = 1 de forma predeterminada; si se desactiva esta configuración, no se admiten las lecturas de JSON. ClickHouse analiza y normaliza JSON, por lo que el texto devuelto puede diferir del texto insertado en el orden de las claves, los espacios en blanco y el formato de los números.

Tipos geométricos

Geometry se expone como una IVariantColumn que admite los seis tipos geométricos.

Tipo QBit

El client TCP admite la variante QBit(T, N) de dos argumentos, sin stride. La disposición de tres argumentos con stride no es compatible.

Tipos AggregateFunction

Una columna AggregateFunction contiene un estado intermedio específico de la función. Obtenga el resultado final en el servidor, por ejemplo, con sumMerge(column), y lea el valor resultante.

Correspondencia de tipos: escritura en ClickHouse

Cada entrada indica el tipo CLR de una fila de la columna IColumn pasada a InsertAsync. Estas correspondencias son exactas: el client TCP no realiza conversiones implícitas como las de Convert.

Tipos de enteros

Tipos de coma flotante

Al escribir valores BFloat16, cada valor float se trunca a la representación de coma flotante de 16 bits conocida como «brain floating point».

Tipos decimales

La operación de escritura lanza una OverflowException si la mantisa escalada supera la precisión declarada.

Tipo booleano

Tipos String

FixedString(N) no admite string por diseño; codifique el valor y añádale el relleno necesario de forma explícita para que su representación binaria exacta no dé lugar a ambigüedades.

Tipos de fecha y hora

Los valores enteros sin procesar usan las unidades indicadas en la tabla de lectura. DateTimeOffset conserva el instante de tiempo. DateTime tiene en cuenta su Kind; un valor Unspecified se interpreta como la hora del reloj en la zona horaria de la columna o, si esta no especifica ninguna, en la zona horaria de la sesión.

Tipos enumerados

Las etiquetas desconocidas se rechazan antes de enviar los datos.

Otros tipos escalares

Tipos compuestos

La estructura aceptada se define de forma recursiva: Array(Nullable(DateTime)) acepta un DateTime?[] por fila y Map(String, UInt32) acepta un KeyValuePair<string, uint>[]. La escritura directa de Nested utiliza la estructura de columnas densas devuelta por una lectura anterior. Para las inserciones generadas por la aplicación, mantenga la representación predeterminada flatten_nested = 1 y escriba cada campo Array(T) cuyo nombre contenga un punto como una columna independiente.

Tipos Variant, Dynamic y JSON

Para Variant, el tipo en tiempo de ejecución debe corresponderse de forma unívoca con el tipo CLR predeterminado de una de las alternativas. Las correspondencias de tipos por conveniencia, como escribir un DateTime en una alternativa DateTime, no se aplican dentro de una variante; utilice en su lugar el tipo sin procesar predeterminado de la alternativa. Dynamic admite valores escalares, así como valores de arrays, mapas y tuplas admitidos de forma recursiva.

Tipos geométricos

En una columna Geometry creada por la aplicación, solo Point y MultiPolygon tienen estructuras CLR exclusivas. Ring y LineString comparten la misma estructura, al igual que Polygon y MultiLineString, por lo que el client no puede inferir qué alternativa representa un valor object. Una columna Geometry decodificada conserva sus discriminadores y puede volver a insertarse sin esa ambigüedad.

Tipo QBit

Tipos AggregateFunction

Compresión

El client activa la compresión de forma predeterminada y usa LZ4 para los bloques que escribe; ClickHouse también usa LZ4 de forma predeterminada para los bloques de respuesta. La compresión se aplica a los bloques de resultados y a los bloques enviados mediante una inserción, mientras que el texto de la consulta, la información de progreso, las excepciones y los demás paquetes del protocolo mantienen su codificación wire habitual. Utilice la clave Compression de la cadena de conexión para elegir el códec que usa el client:
La configuración equivalente con tipos definidos explícitamente utiliza IClickHouseCompressor:
Establezca Compressor = null para desactivar la compresión. Un compresor personalizado debe admitir la delimitación de bloques del protocolo nativo; los compresores que solo admiten cuerpos de solicitudes y respuestas HTTP se rechazan al crear el client.

Selección del códec de respuesta

Compressor determina cómo se comprimen los bloques que escribe el client. El paquete de consulta nativo solicita compresión al servidor, pero no especifica un códec. Por eso, ClickHouse selecciona el códec de respuesta según el ajuste network_compression_method. El códec predeterminado del servidor es LZ4. Configure el códec de respuesta para cada operación cuando sea necesario:
LZ4 suele consumir menos CPU y es la mejor opción predeterminada en redes rápidas. ZSTD suele generar tramas más pequeñas, aunque consume algo más de CPU. Desactivar la compresión puede ser útil si el servidor está en el mismo equipo o se conecta a través de otro enlace muy rápido. Mida el rendimiento con datos representativos antes de cambiar la opción predeterminada.

Gestión de errores

Los fallos notificados por ClickHouse o por la conexión nativa derivan de ClickHouseTcpException, que, a su vez, deriva de DbException: ClickHouseTcpServerException expone tanto RawCode, el código numérico exacto enviado por el servidor, como Code, el ClickHouseErrorCode correspondiente si el driver tiene un nombre para ese código. También incluye el Name de la excepción del servidor, ServerStackTrace y las excepciones anidadas del servidor, si las hay, a través de InnerException.
Los códigos que no están representados por ClickHouseErrorCode tienen Code == ClickHouseErrorCode.Unknown; consulte RawCode sin dar por hecho que la enumeración incluye todos los errores de ClickHouse. Los errores de validación de argumentos y del ciclo de vida de los objetos mantienen sus tipos de excepción habituales de .NET. La cancelación lanza OperationCanceledException. DialTimeout y ReadTimeout lanzan ClickHouseTcpConnectionException con una excepción interna TimeoutException, y la conexión se descarta. PoolTimeout lanza un TimeoutException simple, que no es un ClickHouseTcpException: no ha fallado ninguna conexión, y la solución es reducir las operaciones simultáneas, ampliar el grupo o terminar de enumerar los resultados.

Reintento de operaciones

Si falla la conexión o se agota el tiempo de espera durante una inserción, no se sabe si la operación se completó: el servidor podría haber confirmado las filas antes de que el client dejara de recibir la respuesta. Asigne un DeduplicationToken estable al lote lógico antes de reintentar la operación:
Reutilice el mismo token en los reintentos de ese lote y uno distinto para los datos nuevos. Para las demás operaciones, vuelva a intentarlo solo si la sentencia puede repetirse sin riesgo y el fallo concreto es transitorio; la mayoría de los errores del servidor requieren modificar la consulta o la configuración.

Registro y diagnóstico

El client TCP se integra con Microsoft.Extensions.Logging. El registro es opcional; si no se configura LoggerFactory, el cliente no crea registradores ni da formato a los mensajes de registro.
Cuando se registra el client mediante las extensiones de inyección de dependencias, se utiliza la ILoggerFactory del contenedor, salvo que ya se haya especificado una en las opciones proporcionadas.

Categorías de registro

Configure las categorías mediante la configuración estándar de registro de .NET:
El mensaje de inicio de la operación se registra con el nivel Debug e incluye, como máximo, StatementMaxLength caracteres de SQL. El valor predeterminado es de cinco caracteres. Establezca el valor en cero o menos para que el texto de la sentencia no aparezca en los logs, o auméntelo solo después de tener en cuenta los parámetros y literales que puedan contener datos confidenciales.

Mensajes del log del servidor

La integración de logging registra el comportamiento del cliente. Los paquetes del registro de consultas de ClickHouse son independientes y se envían a ClickHouseTcpQueryCallbacks.OnLog. Para habilitarlos, configure el parámetro del servidor send_logs_level:
El bloque de log se proporciona temporalmente y se libera cuando termina el callback. Procéselo de forma síncrona o copie los valores que necesite conservar. Asegúrese de que los callbacks se ejecuten rápidamente y no lancen excepciones; una excepción en un callback interrumpe la operación y cierra la conexión.

OpenTelemetry

El cliente TCP emite spans de trazas distribuidas a través de la API System.Diagnostics.Activity de .NET. Suscríbase a la fuente de actividades TCP mediante un proveedor de trazas de OpenTelemetry:
Para una aplicación de consola o una configuración manual:
El nombre de la fuente de actividades es ClickHouse.Driver.Tcp, distinto del de la fuente del cliente HTTP, por lo que se pueden recopilar datos de cada transporte por separado. Si no hay ningún listener suscrito, las operaciones no crean actividades.

Spans y atributos

El client emite spans para las operaciones SQL, los pings y las conexiones nuevas. Los spans SQL toman el nombre de la palabra clave con la que comienza la sentencia, como SELECT o INSERT; si no se reconoce una palabra clave inicial, se usa query. Un span connect abarca la conexión mediante socket, la negociación TLS y el handshake nativo. Los spans de sentencias también incluyen el tiempo de espera hasta obtener una conexión del pool. Los contadores solo están presentes cuando el servidor o el client disponen de un valor significativo que comunicar. Por ejemplo, una inserción que carga bloques indica las filas enviadas por el client, pero no recibe contadores de lectura del servidor. Los spans fallidos también contienen un evento exception con el tipo de excepción, el mensaje y la traza de pila.

Texto SQL y datos sensibles

De forma predeterminada, los spans no incluyen el texto SQL. Habilite su inclusión de forma explícita y establezca un límite adecuado:
StatementMaxLength limita la longitud del atributo db.query.text y del texto SQL en los logs de depuración. Un valor de cero o menos hace que el texto SQL se omita en ambos casos. Revise también los mensajes de excepción y las trazas de pila exportados, ya que los errores del servidor pueden incluir detalles de las consultas.

Propagación del contexto de traza

Cuando Activity.Current usa el formato de ID del W3C, el client incluye el ID de traza, el ID de span, los indicadores de traza y el estado de la traza en el paquete de consulta nativo. ClickHouse puede así crear spans de servidor dentro de la misma traza distribuida. La recopilación y el muestreo de spans del lado del servidor se configuran en ClickHouse; por ejemplo, la probabilidad de muestreo se puede establecer para cada consulta:
El ID de la consulta propagado puede utilizarse junto con el ID de traza para correlacionar el span del client con system.query_log y, si está configurado, con system.opentelemetry_span_log de ClickHouse.

Limitaciones

El client TCP es experimental y no ofrece todas las funciones del client HTTP.

Funcionalidades disponibles solo por HTTP

La API TCP no es un proveedor de ADO.NET y no puede utilizarse con ORM. Tampoco admite la entrada ni la salida en CSV, JSONEachRow, Parquet o flujos sin procesar; la autenticación bearer ni los encabezados HTTP personalizados; la resolución personalizada de tipos de parámetros, el formateo o la conversión de lectura; ni la especificación de una base de datos o un rol por consulta. Use el cliente HTTP si una aplicación necesita alguna de estas funcionalidades.

Parámetros SQL

Solo se admiten los marcadores de posición {name:Type} propios de ClickHouse; el cliente TCP no transforma los marcadores de posición @name de ADO.NET. En las versiones de ClickHouse de la 25.8 a la 26.6, los nombres de parámetros que coincidan con opciones de configuración del servidor, como limit u offset, pueden interpretarse como tales y rechazarse. Si necesita compatibilidad con esas versiones, cámbieles el nombre, por ejemplo, a row_limit.

Cobertura de tipos de datos

La sección tipos de datos compatibles detalla todas las correspondencias. El códec nativo actual presenta estas limitaciones principales:
  • Las tuplas pueden contener como máximo siete elementos.
  • Los estados intermedios de AggregateFunction(function, ...) no son compatibles. Finalícelos en el servidor, por ejemplo con sumMerge(column), y lea el resultado.
  • Solo se admite la disposición QBit(T, N) de dos argumentos y sin partición por stride.
  • JSON utiliza la serialización String de ClickHouse y requiere output_format_native_write_json_as_string = 1, que el cliente solicita de forma predeterminada.
  • Las inserciones generadas por la aplicación no tienen una representación orientada a filas para Nested(...) cuando flatten_nested = 0. Utilice en su lugar las columnas Array(T) aplanadas, con nombres separados por puntos, que se usan de forma predeterminada.
  • Una columna Geometry generada por la aplicación no permite distinguir entre tipos que comparten la misma estructura CLR, como Ring y LineString. Inserte el tipo de geometría concreto o vuelva a insertar una columna Geometry decodificada que conserve sus discriminadores.

Reintentos automáticos

El client no reintenta automáticamente las operaciones fallidas. Reintente solo las operaciones que sea seguro repetir y establezca un DeduplicationToken estable antes de reintentar una inserción cuyo resultado se desconozca. Consulte cómo reintentar operaciones para obtener más información.
Última modificación el 14 de octubre de 2026