ClickHouse C# 客户端
用于连接 ClickHouse 的官方 C# 客户端。 客户端源代码托管在 GitHub 仓库 中。 最初由 Oleg V. Kozlyuk 开发。
迁移指南
- 在
.csproj文件中将包名更新为ClickHouse.Driver,并将版本更新为 NuGet 上的最新版本。 - 将代码库中所有对
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 安装该软件包:
或者使用 NuGet 包管理器:
快速入门
配置
配置与 ClickHouse 的连接有两种方式:
- 连接字符串: 以分号分隔的键值对,用于指定主机、身份验证凭据以及其他连接选项。
ClickHouseClientSettings对象: 强类型的配置对象,可以从配置文件加载或在代码中设置。
下面是所有配置项的完整列表,包括它们的默认值及其作用。
连接设置
| 属性 | 类型 | 默认值 | 连接字符串键 | 描述 |
|---|---|---|---|---|
| Host | string | "localhost" | Host | ClickHouse 服务器的主机名或 IP 地址 |
| Port | ushort | 8123 (HTTP) / 8443 (HTTPS) | Port | 端口号;默认值取决于协议 |
| Username | string | "default" | Username | 身份验证用户名 |
| Password | string | "" | Password | 身份验证密码 |
| Database | string | "" | Database | 默认数据库;为空时使用服务器/用户的默认值 |
| Protocol | string | "http" | Protocol | 连接协议:"http" 或 "https" |
| Path | string | null | Path | 用于反向代理场景的 URL 路径(例如 /clickhouse) |
| Timeout | TimeSpan | 2 分钟 | Timeout | 操作超时时间(在连接字符串中以秒为单位存储) |
数据格式与序列化
| 属性 | 类型 | 默认值 | 连接字符串键名 | 描述 |
|---|---|---|---|---|
| UseCompression | bool | true | Compression | 为数据传输启用 gzip 压缩 |
| UseCustomDecimals | bool | true | UseCustomDecimals | 使用 ClickHouseDecimal 处理任意精度小数;如果为 false,则使用 .NET decimal(128 位上限) |
| UseFormDataParameters | bool | false | UseFormDataParameters | 将参数以表单数据的形式发送,而不是作为 URL 查询字符串 |
会话管理
| 属性 | 类型 | 默认值 | 连接字符串键 | 描述 |
|---|---|---|---|---|
| UseSession | bool | false | UseSession | 启用有状态会话;将请求串行执行 |
| SessionId | string | null | SessionId | 会话 ID;如果为 null 且 UseSession 为 true,则自动生成 GUID |
UseSession 选项启用服务器会话持久化,从而可以使用 SET 语句和临时表。会话在 60 秒无活动后将被重置(默认超时时间)。可以通过在 ClickHouse 语句中设置会话相关配置或通过服务器配置来延长会话生命周期。
ClickHouseConnection 类通常允许并行操作(多个线程可以并发执行查询)。但是,启用 UseSession 选项后,在任意时刻,每个连接只能有一个活动查询(这是服务器端限制)。
安全
| 属性 | 类型 | 默认值 | 连接字符串键 | 说明 |
|---|---|---|---|---|
| SkipServerCertificateValidation | bool | false | — | 跳过 HTTPS 证书验证;不应用于生产环境 |
HTTP 客户端配置
| 属性 | 类型 | 默认值 | 连接字符串键 | 说明 |
|---|---|---|---|---|
| HttpClient | HttpClient | null | — | 自定义的预配置 HttpClient 实例 |
| HttpClientFactory | IHttpClientFactory | null | — | 用于创建 HttpClient 实例的自定义工厂 |
| HttpClientName | string | null | — | 供 HttpClientFactory 在创建特定客户端时使用的名称 |
日志与调试
| 属性 | 类型 | 默认值 | 连接字符串键 | 描述 |
|---|---|---|---|---|
| LoggerFactory | ILoggerFactory | null | — | 用于诊断日志记录的 LoggerFactory 工厂 |
| EnableDebugMode | bool | false | — | 启用 .NET 网络跟踪(需要配置 LoggerFactory 且日志级别设置为 Trace);对性能有显著影响 |
自定义设置与角色
| Property | Type | Default | Connection String Key | Description |
|---|---|---|---|---|
| CustomSettings | IDictionary<string, object> | Empty | set_* prefix | ClickHouse 服务器设置,参见下方说明 |
| Roles | IReadOnlyList<string> | Empty | Roles | 用逗号分隔的 ClickHouse 角色(例如:Roles=admin,reader) |
使用 connection string 配置自定义设置时,请使用 set_ 前缀,例如 set_max_threads=4。使用 ClickHouseClientSettings 对象时,不要使用 set_ 前缀。
有关可用设置的完整列表,请参见此处。
连接字符串示例
基本连接
使用自定义 ClickHouse 配置
使用方法
连接
若要连接 ClickHouse,请通过连接字符串或 ClickHouseClientSettings 对象创建一个 ClickHouseConnection。有关可用选项,请参见 Configuration 部分。
ClickHouse Cloud 服务的连接信息可以在 ClickHouse Cloud 控制台中获取。
选择某个服务并单击 Connect:

选择 C#,连接信息会显示在下方。

如果您使用的是自管理 ClickHouse,则连接信息由您的 ClickHouse 管理员配置。
使用连接字符串:
也可以使用 ClickHouseClientSettings:
- 一个
ClickHouseConnection表示与服务器的“会话”。它会通过查询服务器版本来探测可用特性(因此在打开连接时会有轻微的开销),但通常情况下,多次创建和销毁此类对象是安全的。 - 推荐的连接生命周期是:针对一个跨越多个查询的大型“事务”,使用一个连接对象。
ClickHouseConnection对象可以长时间存在。由于连接建立时会有少量开销,因此不建议为每个查询都创建一个新的连接对象。 - 如果应用程序处理大量事务,并且需要频繁创建/销毁
ClickHouseConnection对象,建议使用IHttpClientFactory或静态的HttpClient实例来管理连接。
创建表
使用标准 SQL 语法创建表:
插入数据
使用参数化查询插入数据:
批量插入
使用 ClickHouseBulkCopy 来插入大量数据行。它使用 ClickHouse 的原生行二进制格式高效地流式传输数据,支持并行插入,并且可以将数据拆分为批次。同时,它还可以避免由于参数集过大而导致的“URL too long”错误。
使用 ClickHouseBulkCopy 时需要:
- 目标连接(
ClickHouseConnection实例) - 目标表名(
DestinationTableName属性) - 数据源(
IDataReader或IEnumerable<object[]>)
- 为获得最佳性能,
ClickHouseBulkCopy使用 Task Parallel Library (TPL) 处理批量数据,最多可并行执行 4 个插入任务(此值可调)。 - 如果源数据的列数少于目标表的列数,可以通过
ColumnNames属性可选地指定列名。 - 可配置参数:
Columns、BatchSize、MaxDegreeOfParallelism。 - 在复制之前,会执行
SELECT * FROM <table> LIMIT 0查询以获取目标表结构信息。要写入的对象类型必须与目标表结构合理匹配。 - 会话与并行插入不兼容。传递给
ClickHouseBulkCopy的连接必须禁用会话,或者将MaxDegreeOfParallelism设置为1。
执行 SELECT 查询
使用 ExecuteReader() 或 ExecuteReaderAsync() 执行 SELECT 查询。返回的 DbDataReader 通过 GetInt64()、GetString() 和 GetFieldValue<T>() 等方法,为结果列提供类型安全的访问。
调用 Read() 以移动到下一行。当没有更多行时,它返回 false。可以通过索引(从 0 开始)或列名访问列。
SQL 参数
在 ClickHouse 中,SQL 查询参数的标准格式为 {parameter_name:DataType}。
示例:
SQL bind 参数作为 HTTP URI 查询参数传递,因此如果使用过多,可能会触发 “URL too long” 异常。使用 ClickHouseBulkInsert 可以绕过此限制。
查询 ID
每个发起查询的方法都会在结果中包含一个 query_id。该唯一标识符由客户端为每个查询分配,可用于从 system.query_log 表中获取数据(如果已启用),或取消长时间运行的查询。如有需要,用户可以在 ClickHouseCommand 对象中自定义该查询 ID。
如果要覆盖 QueryId 参数,必须确保它在每次调用中都是唯一的。使用随机生成的 GUID 是一个不错的选择。
原始数据流
可以直接以特定格式对数据进行流式传输,从而绕过数据读取器。这在您希望以特定格式将数据保存到文件时非常有用。例如:
原始流插入
使用 InsertRawStreamAsync 将数据直接从文件或内存流中插入,格式可以是 CSV、JSON,或任意ClickHouse 支持的格式。
从 CSV 文件插入:
有关用于控制数据摄取行为的选项,请参阅格式设置文档。
更多示例
有关更多实用示例,请参阅 GitHub 仓库中的 examples 目录。
最佳实践
连接生命周期与连接池
ClickHouse.Driver 在底层使用 System.Net.Http.HttpClient。HttpClient 会针对每个端点维护一个连接池。因此:
- 一个
ClickHouseConnection对象并不是与 TCP 连接一一对应——多个数据库会话会通过每台服务器上的多个 TCP 连接复用。 ClickHouseConnection对象本身可以是长生命周期的;其底层实际使用的 TCP 连接会由连接池自动回收与复用。- 让
HttpClient在内部管理连接池。不要自行对ClickHouseConnection对象进行池化管理。 - 即使
ClickHouseConnection对象已被释放,连接依然可能保持存活。 - 可以通过传入自定义的
HttpClientFactory,或带有自定义HttpClientHandler的HttpClient来调整此行为。
对于依赖注入(DI)环境,提供了一个专用构造函数 ClickHouseConnection(string connectionString, IHttpClientFactory httpClientFactory, string httpClientName = ""),用于让 ClickHouseConnection 请求一个具名的 HTTP 客户端。
在使用自定义 HttpClient 或 HttpClientFactory 时,确保将 PooledConnectionIdleTimeout 设置为小于服务器 keep_alive_timeout 的值,以避免由于半关闭的连接导致的错误。Cloud 部署的默认 keep_alive_timeout 是 10 秒。
DateTime 处理
-
尽可能使用 UTC。 将时间戳存储为
DateTime('UTC')列,并在代码中使用DateTimeKind.Utc,以消除时区歧义。 -
使用
DateTimeOffset进行显式时区处理。 它始终表示一个确切的时间点,并包含偏移量信息。 -
在 HTTP 参数类型提示中指定时区。 当向非 UTC 的列传递带有
UnspecifiedDateTime 值的参数时:
异步插入
异步插入 将批处理的职责从客户端转移到服务器。服务器无需客户端进行批处理,而是自行对传入数据进行缓冲,并根据可配置阈值将其写入存储。这对于高并发场景(例如可观测性负载中有许多代理程序发送小体量数据的情况)非常有用。
可以通过 CustomSettings 或连接字符串启用异步插入:
两种模式(由 wait_for_async_insert 控制):
| 模式 | 行为 | 使用场景 |
|---|---|---|
wait_for_async_insert=1 | 在数据刷写到磁盘后才返回 INSERT。错误会返回给客户端。 | 推荐用于大多数工作负载 |
wait_for_async_insert=0 | 在数据进入缓冲区后立即返回 INSERT。不保证数据一定会被持久化。 | 仅在可以接受数据丢失时使用 |
在 wait_for_async_insert=0 的情况下,错误只会在刷写阶段暴露,且无法追溯到最初的 INSERT。客户端也不会提供背压机制,存在导致服务器过载的风险。
关键设置:
| 设置 | 描述 |
|---|---|
async_insert_max_data_size | 当缓冲区达到此大小(字节)时触发刷写 |
async_insert_busy_timeout_ms | 在达到此超时时间(毫秒)后触发刷写 |
async_insert_max_query_number | 当累计达到此数量的查询后触发刷写 |
会话
仅在需要有状态的服务端功能时才启用会话,例如:
- 临时表(
CREATE TEMPORARY TABLE) - 在多个语句之间保持查询上下文
- 会话级设置(
SET max_threads = 4)
启用会话后,请求会被串行化,以防止同一会话被并发使用。对于不需要会话状态的工作负载,这会引入额外的开销。
支持的数据类型
ClickHouse.Driver 支持所有 ClickHouse 数据类型。下表展示了从数据库读取数据时,ClickHouse 类型与原生 .NET 类型之间的映射关系。
类型映射:从 ClickHouse 读取数据
整数类型
| ClickHouse 类型 | .NET 类型 |
|---|---|
| Int8 | sbyte |
| UInt8 | byte |
| Int16 | short |
| UInt16 | ushort |
| Int32 | int |
| UInt32 | uint |
| Int64 | long |
| UInt64 | ulong |
| Int128 | BigInteger |
| UInt128 | BigInteger |
| Int256 | BigInteger |
| UInt256 | BigInteger |
浮点数类型
| ClickHouse 类型 | .NET 类型 |
|---|---|
| Float32 | float |
| Float64 | double |
| BFloat16 | float |
Decimal 类型
| ClickHouse Type | .NET Type |
|---|---|
| Decimal(P, S) | decimal / ClickHouseDecimal |
| Decimal32(S) | decimal / ClickHouseDecimal |
| Decimal64(S) | decimal / ClickHouseDecimal |
| Decimal128(S) | decimal / ClickHouseDecimal |
| Decimal256(S) | decimal / ClickHouseDecimal |
Decimal 类型转换由 UseCustomDecimals 设置项控制。
布尔类型
| ClickHouse 类型 | .NET 类型 |
|---|---|
| Bool | bool |
字符串类型
| ClickHouse 类型 | .NET 类型 |
|---|---|
| String | string |
| FixedString(N) | byte[] |
日期和时间类型
| ClickHouse Type | .NET Type |
|---|---|
| Date | DateTime |
| Date32 | DateTime |
| DateTime | DateTime |
| DateTime32 | DateTime |
| DateTime64 | DateTime |
| Time | TimeSpan |
| Time64 | TimeSpan |
ClickHouse 在内部将 DateTime 和 DateTime64 值存储为 Unix 时间戳(自 Unix 纪元起的秒或子秒单位)。虽然存储始终为 UTC,但列可以关联一个时区,这会影响值的显示和解释方式。
读取 DateTime 值时,会根据列的时区设置 DateTime.Kind 属性:
| Column Definition | Returned DateTime.Kind | Notes |
|---|---|---|
DateTime('UTC') | Utc | 显式 UTC 时区 |
DateTime('Europe/Amsterdam') | Unspecified | 已应用偏移量 |
DateTime | Unspecified | 按原样保留墙上时钟时间(本地时刻) |
对于非 UTC 时区的列,返回的 DateTime 表示该时区中的墙上时钟时间。使用 ClickHouseDataReader.GetDateTimeOffset() 获取带有该时区正确偏移量的 DateTimeOffset:
对于没有显式时区的列(即使用 DateTime 而不是 DateTime('Europe/Amsterdam')),驱动会返回一个 Kind=Unspecified 的 DateTime。这样可以在不对时区做任何假设的前提下,精确保留与存储值一致的挂钟时间(wall-clock time)。
如果你需要对没有显式时区的列实现具备时区感知的行为,可以:
- 在列定义中使用显式时区:
DateTime('UTC')或DateTime('Europe/Amsterdam') - 在读取之后自行应用时区转换。
其他类型
| ClickHouse 类型 | .NET 类型 |
|---|---|
| UUID | Guid |
| IPv4 | IPAddress |
| IPv6 | IPAddress |
| Nothing | DBNull |
| Dynamic | 参见注释 |
| Json | JsonObject |
| Array(T) | T[] |
| Tuple(T1, T2, ...) | Tuple<T1, T2, ...> / LargeTuple |
| Map(K, V) | Dictionary<K, V> |
| Nullable(T) | T? |
| Enum8 | string |
| Enum16 | string |
| LowCardinality(T) | 与 T 相同 |
| SimpleAggregateFunction | 与底层类型相同 |
| Nested(...) | Tuple[] |
| Variant(T1, T2, ...) | 参见注释 |
| QBit(T, dimension) | T[] |
Dynamic 和 Variant 类型会根据每一行中实际的底层类型转换为对应的类型。
几何类型
| ClickHouse Type | .NET Type |
|---|---|
| Point | Tuple<double, double> |
| Ring | Tuple<double, double>[] |
| LineString | Tuple<double, double>[] |
| Polygon | Ring[] |
| MultiLineString | LineString[] |
| MultiPolygon | Polygon[] |
| Geometry | 参见说明 |
Geometry 类型是一个 Variant 类型,可以包含任意几何类型。它会被转换为对应的类型。
类型映射:写入 ClickHouse
在插入数据时,驱动会将 .NET 类型转换为相应的 ClickHouse 类型。下表展示了每种 ClickHouse 列类型可以接受的 .NET 类型。
整数类型
| ClickHouse 类型 | 可接受的 .NET 类型 | 备注 |
|---|---|---|
| Int8 | sbyte 以及任何与 Convert.ToSByte() 兼容的类型 | |
| UInt8 | byte 以及任何与 Convert.ToByte() 兼容的类型 | |
| Int16 | short 以及任何与 Convert.ToInt16() 兼容的类型 | |
| UInt16 | ushort 以及任何与 Convert.ToUInt16() 兼容的类型 | |
| Int32 | int 以及任何与 Convert.ToInt32() 兼容的类型 | |
| UInt32 | uint 以及任何与 Convert.ToUInt32() 兼容的类型 | |
| Int64 | long 以及任何与 Convert.ToInt64() 兼容的类型 | |
| UInt64 | ulong 以及任何与 Convert.ToUInt64() 兼容的类型 | |
| Int128 | BigInteger、decimal、double、float、int、uint、long、ulong,以及任何与 Convert.ToInt64() 兼容的类型 | |
| UInt128 | BigInteger、decimal、double、float、int、uint、long、ulong,以及任何与 Convert.ToInt64() 兼容的类型 | |
| Int256 | BigInteger、decimal、double、float、int、uint、long、ulong,以及任何与 Convert.ToInt64() 兼容的类型 | |
| UInt256 | BigInteger、decimal、double、float、int、uint、long、ulong,以及任何与 Convert.ToInt64() 兼容的类型 |
浮点类型
| ClickHouse Type | 可接受的 .NET 类型 | 说明 |
|---|---|---|
| Float32 | float,任何与 Convert.ToSingle() 兼容的类型 | |
| Float64 | double,任何与 Convert.ToDouble() 兼容的类型 | |
| BFloat16 | float,任何与 Convert.ToSingle() 兼容的类型 | 截断为 16 位 bfloat16 格式 |
布尔类型
| ClickHouse 类型 | 可接受的 .NET 类型 | 备注 |
|---|---|---|
| Bool | bool |
字符串类型
| ClickHouse 类型 | 可接受的 .NET 类型 | 说明 |
|---|---|---|
| String | string,任何与 Convert.ToString() 兼容的类型 | |
| FixedString(N) | string,byte[] | 字符串以 UTF-8 编码,并会进行填充或截断;byte[] 的长度必须正好为 N 字节 |
日期和时间类型
| ClickHouse Type | 可接受的 .NET 类型 | 说明 |
|---|---|---|
| Date | DateTime, DateTimeOffset, DateOnly, NodaTime 类型 | 转换为自 Unix 纪元起的天数,类型为 UInt16 |
| Date32 | DateTime, DateTimeOffset, DateOnly, NodaTime 类型 | 转换为自 Unix 纪元起的天数,类型为 Int32 |
| DateTime | DateTime, DateTimeOffset, DateOnly, NodaTime 类型 | 详细行为见下文 |
| DateTime32 | DateTime, DateTimeOffset, DateOnly, NodaTime 类型 | 与 DateTime 相同 |
| DateTime64 | DateTime, DateTimeOffset, DateOnly, NodaTime 类型 | 精度由 Scale 参数决定 |
| Time | TimeSpan, int | 限制在 ±999:59:59 范围内;int 按秒解释 |
| Time64 | TimeSpan, decimal, double, float, int, long, string | string 按 [-]HHH:MM:SS[.fraction] 解析;限制在 ±999:59:59.999999999 范围内 |
在写入值时,驱动会遵循 DateTime.Kind:
DateTime.Kind | 行为 |
|---|---|
Utc | 精确保留同一时间点 |
Local | 使用系统时区转换为 UTC,同时精确保留时间点 |
Unspecified | 按目标列所在时区的本地墙上时间(本地时钟时间)处理 |
DateTimeOffset 值始终精确保留时间点。
示例:UTC DateTime(时间点保持不变)
示例:未指定 DateTime(墙上时钟时间)
建议: 为获得最简单且最可预测的行为,请在所有与 DateTime 相关的操作中使用 DateTimeKind.Utc 或 DateTimeOffset。这样可以确保无论服务器时区、客户端时区还是列时区如何,代码都能保持一致的运行方式。
HTTP 参数与批量复制
在写入 Unspecified 的 DateTime 值时,通过 HTTP 参数绑定和通过批量复制之间存在一个重要区别:
Bulk Copy 知道目标列的时区,并会在该时区中正确解释 Unspecified 值。
HTTP Parameters 无法自动获知列的时区。你必须在参数类型提示中显式指定该时区:
DateTime.Kind | 目标列 | HTTP 参数(带时区提示) | HTTP 参数(无时区提示) | 批量复制 |
|---|---|---|---|---|
Utc | UTC | 时间点保持不变 | 时间点保持不变 | 时间点保持不变 |
Utc | Europe/Amsterdam | 时间点保持不变 | 时间点保持不变 | 时间点保持不变 |
Local | 任意 | 时间点保持不变 | 时间点保持不变 | 时间点保持不变 |
Unspecified | UTC | 视为 UTC | 视为 UTC | 视为 UTC |
Unspecified | Europe/Amsterdam | 视为阿姆斯特丹时间 | 视为 UTC | 视为阿姆斯特丹时间 |
Decimal 类型
| ClickHouse 类型 | 可接受的 .NET 类型 | 说明 |
|---|---|---|
| Decimal(P,S) | decimal, ClickHouseDecimal, 任意与 Convert.ToDecimal() 兼容的类型 | 当超出精度时抛出 OverflowException |
| Decimal32 | decimal, ClickHouseDecimal, 任意与 Convert.ToDecimal() 兼容的类型 | 最大精度为 9 |
| Decimal64 | decimal, ClickHouseDecimal, 任意与 Convert.ToDecimal() 兼容的类型 | 最大精度为 18 |
| Decimal128 | decimal, ClickHouseDecimal, 任意与 Convert.ToDecimal() 兼容的类型 | 最大精度为 38 |
| Decimal256 | decimal, ClickHouseDecimal, 任意与 Convert.ToDecimal() 兼容的类型 | 最大精度为 76 |
其他类型
| ClickHouse 类型 | 可接受的 .NET 类型 | 说明 |
|---|---|---|
| UUID | Guid, string | 字符串将被解析为 Guid |
| IPv4 | IPAddress, string | 必须为 IPv4;字符串通过 IPAddress.Parse() 解析 |
| IPv6 | IPAddress, string | 必须为 IPv6;字符串通过 IPAddress.Parse() 解析 |
| Nothing | 任意类型 | 不写入任何内容(no-op) |
| Dynamic | — | 不支持(抛出 NotImplementedException) |
| Json | string, JsonObject, 任意对象 | 字符串将被解析为 JSON;对象通过 JsonSerializer 序列化 |
| Array(T) | IList, null | 将 null 写为空数组 |
| Tuple(T1, T2, ...) | ITuple, IList | 元素数量必须与元组的元数一致 |
| Map(K, V) | IDictionary | |
| Nullable(T) | null, DBNull,或 T 可接受的类型 | 在值前写入一个 null 标志字节 |
| Enum8 | string, sbyte, 数值类型 | 字符串将在枚举字典中查找对应值 |
| Enum16 | string, short, 数值类型 | 字符串将在枚举字典中查找对应值 |
| LowCardinality(T) | T 可接受的类型 | 委托给底层类型 |
| SimpleAggregateFunction | 底层类型可接受的类型 | 委托给底层类型 |
| Nested(...) | 元组组成的 IList | 元素数量必须与字段数量一致 |
| Variant(T1, T2, ...) | 匹配 T1、T2 等之一的值 | 如果没有类型匹配则抛出 ArgumentException |
| QBit(T, dim) | IList | 委托给 Array;维度仅作为元数据存在 |
几何类型
| ClickHouse Type | 接受的 .NET 类型 | 说明 |
|---|---|---|
| Point | System.Drawing.Point、ITuple、IList(含 2 个元素) | |
| Ring | Point 的 IList | |
| LineString | Point 的 IList | |
| Polygon | Ring 的 IList | |
| MultiLineString | LineString 的 IList | |
| MultiPolygon | Polygon 的 IList | |
| Geometry | 以上任意几何类型 | 包含所有几何类型的变体 |
不支持写入
| ClickHouse 类型 | 说明 |
|---|---|
| Dynamic | 会抛出 NotImplementedException |
| AggregateFunction | 会抛出 AggregateFunctionException |
嵌套类型处理
ClickHouse 嵌套类型(Nested(...))可以按照数组语义进行读写。
日志和诊断
ClickHouse .NET 客户端与 Microsoft.Extensions.Logging 抽象层集成,提供轻量级、可选启用的日志记录功能。启用后,驱动程序会针对连接生命周期事件、命令执行、传输操作以及批量复制上传输出结构化消息。日志记录完全是可选的——未配置记录器的应用程序将继续正常运行,并且不会引入任何额外开销。
快速开始
使用 ClickHouseConnection
使用 appsettings.json
可以使用标准的 .NET 配置来配置日志级别:
使用内存配置
你也可以在代码中按类别配置日志的详细程度:
分类与发射源
驱动程序使用专用日志分类,便于按组件精细调整日志级别:
| 分类 | 源 | 要点 |
|---|---|---|
ClickHouse.Driver.Connection | ClickHouseConnection | 连接生命周期、HTTP 客户端工厂的选择、连接打开/关闭、会话管理。 |
ClickHouse.Driver.Command | ClickHouseCommand | 查询执行开始/完成、计时、查询 ID、服务器统计信息和错误详情。 |
ClickHouse.Driver.Transport | ClickHouseConnection | 底层 HTTP 流式请求、压缩标志、响应状态码以及传输失败情况。 |
ClickHouse.Driver.BulkCopy | ClickHouseBulkCopy | 元数据加载、批量操作、行计数以及上传完成情况。 |
ClickHouse.Driver.NetTrace | TraceHelper | 网络跟踪,仅在启用调试模式时生效。 |
示例:排查连接问题
这将记录:
- HTTP 客户端工厂的选择(默认连接池 vs 单连接)
- HTTP 处理程序配置(SocketsHttpHandler 或 HttpClientHandler)
- 连接池设置(MaxConnectionsPerServer、PooledConnectionLifetime 等)
- 超时设置(ConnectTimeout、Expect100ContinueTimeout 等)
- SSL/TLS 配置
- 连接打开/关闭事件
- 会话 ID 跟踪
调试模式:网络跟踪与诊断
为帮助诊断网络问题,驱动程序库提供了一个辅助工具,可启用对 .NET 网络内部机制的底层跟踪。要启用它,必须传入一个 LoggerFactory,并将日志级别设置为 Trace,同时将 EnableDebugMode 设置为 true(或者通过 ClickHouse.Driver.Diagnostic.TraceHelper 类手动启用)。日志事件将记录到 ClickHouse.Driver.NetTrace 类别中。警告:这会生成极其冗长的日志,并影响性能。不建议在生产环境中启用调试模式。
OpenTelemetry
该驱动通过 .NET System.Diagnostics.Activity API 提供对 OpenTelemetry 分布式追踪的内置支持。启用后,驱动会为数据库操作生成 spans,这些 spans 可以导出到 Jaeger 等可观测性后端系统,或者通过 OpenTelemetry Collector 导出到 ClickHouse 本身。
启用追踪
在 ASP.NET Core 应用程序中,将 ClickHouse 驱动程序的 ActivitySource 添加到 OpenTelemetry 配置中:
用于控制台应用程序、测试或手动设置:
Span 属性
每个 span 都包含标准的 OpenTelemetry 数据库属性,以及可用于调试的 ClickHouse 特有查询统计信息。
| Attribute | Description |
|---|---|
db.system | 始终为 "clickhouse" |
db.name | 数据库名称 |
db.user | 用户名 |
db.statement | SQL 查询(如果已启用) |
db.clickhouse.read_rows | 查询读取的行数 |
db.clickhouse.read_bytes | 查询读取的字节数 |
db.clickhouse.written_rows | 查询写入的行数 |
db.clickhouse.written_bytes | 查询写入的字节数 |
db.clickhouse.elapsed_ns | 服务器端执行时间(纳秒) |
配置选项
通过 ClickHouseDiagnosticsOptions 控制跟踪行为:
启用 IncludeSqlInActivityTags 可能会在跟踪数据中暴露敏感数据。在生产环境中请谨慎使用。
TLS 配置
通过 HTTPS 连接到 ClickHouse 时,可以使用多种方式配置 TLS/SSL 行为。
自定义证书验证
在生产环境中如需自定义证书验证逻辑,请提供一个自定义的 HttpClient 实例,并配置 ServerCertificateCustomValidationCallback 处理程序:
在提供自定义 HttpClient 时需要注意的重要事项
- 自动解压缩:如果未禁用压缩功能(默认启用压缩),必须启用
AutomaticDecompression。 - 空闲超时:将
PooledConnectionIdleTimeout设置为小于服务器的keep_alive_timeout(ClickHouse Cloud 默认为 10 秒),以避免由于半开连接导致的连接错误。
ORM 支持
Dapper
ClickHouse.Driver 可以与 Dapper 一起使用,但不支持匿名对象。
可运行示例:
不支持:
Linq2db
此驱动程序兼容 linq2db,这是一个用于 .NET 的轻量级 ORM 和 LINQ 提供程序。有关详细文档,请参阅该项目主页。
使用示例:
使用 ClickHouse 提供程序创建一个 DataConnection 实例:
可以使用特性或 fluent 配置来定义表映射。如果类名和属性名与表名和列名完全一致,则无需额外配置:
查询:
批量复制:
使用 BulkCopyAsync 以高效地执行批量插入。
Entity framework core
当前尚不支持 Entity Framework Core。
限制
AggregateFunction 列
类型为 AggregateFunction(...) 的列不能直接进行查询或插入操作。
要进行插入:
选择: