Skip to main content
这是 ClickHouse 的官方 Rust 客户端,最初由 Paul Loyd 开发。客户端源代码可在 GitHub 仓库 中获取。

概述

  • 使用 serde 对行进行编码和解码。
  • 支持 serde 属性:skip_serializingskip_deserializingrename
  • 通过 HTTP 传输使用 RowBinary 格式。
    • 计划切换为通过 TCP 使用 Native 格式。
  • 支持 TLS (通过 native-tlsrustls-tls 功能特性) 。
  • 支持压缩和解压缩 (LZ4) 。
  • 提供用于查询或插入数据、执行 DDL 语句以及进行客户端批处理的 API。
  • 为单元测试提供便捷的 mock。

安装

要使用该 crate,请将以下内容添加到 Cargo.toml
另请参阅:crates.io 页面.

Cargo 特性

  • lz4 (默认启用) — 启用 Compression::Lz4Compression::Lz4Hc(_) Variant。启用后,除 WATCH 外,所有查询默认都使用 Compression::Lz4
  • native-tls — 通过 hyper-tls 支持 HTTPS schema 的 URL,并链接 OpenSSL。
  • rustls-tls — 通过 hyper-rustls 支持 HTTPS schema 的 URL,且不链接 OpenSSL。
  • inserter — 启用 client.inserter()
  • test-util — 添加 mock。参见示例。仅在 dev-dependencies 中使用。
  • watch — 启用 client.watch 功能。详见对应章节。
  • uuid — 添加 serde::uuid,以便配合 uuid crate 使用。
  • time — 添加 serde::time,以便配合 time crate 使用。
通过 HTTPS URL 连接到 ClickHouse 时,应启用 native-tlsrustls-tls 两项特性中的一项。 如果两者都启用,则 rustls-tls 特性具有更高优先次序。

ClickHouse 版本兼容性

该客户端兼容 ClickHouse 长期支持版及更高版本,也兼容 ClickHouse Cloud。 早于 v22.6 的 ClickHouse server 在某些极少数情况下会错误处理 RowBinary。 你可以使用 v0.11+ 并启用 wa-37420 功能来解决此问题。注意:不要将此功能用于较新的 ClickHouse 版本。

示例

我们希望通过客户端代码仓库中的 示例 覆盖客户端使用的各种场景。概览可参见 示例 README 如果示例或下文档中有任何不清楚或缺失的内容,欢迎随时联系我们

用法

ch2rs crate 可用于根据 ClickHouse 生成行类型。

创建客户端实例

复用已创建的客户端,或对其进行克隆,从而复用底层的 hyper 连接池。

HTTPS 或 ClickHouse Cloud 连接

HTTPS 可与 rustls-tlsnative-tls cargo feature 搭配使用。 然后,像平常一样创建 client。在此示例中,使用环境变量存储连接信息:
URL 应同时包含协议和端口,例如 https://instance.clickhouse.cloud:8443
另请参阅:

查询行

  • 占位符 ?fields 会被替换为 no, name (Row 的字段) 。
  • 占位符 ? 会被替换为后续 bind() 调用中传入的值。
  • 可以使用便捷的 fetch_one::<Row>()fetch_all::<Row>() 方法,分别获取第一行或全部行。
  • 可以使用 sql::Identifier 来绑定表名。
注意:由于整个响应是以流式方式返回的,游标即使在已经返回了一些行之后,也仍然可能报错。如果你的用例中遇到这种情况,可以尝试使用 query(...).with_option("wait_end_of_query", "1") 在服务端启用响应缓冲。更多详情buffer_size 选项也可能有帮助。
在查询行时请谨慎使用 wait_end_of_query,因为它会增加服务端的内存消耗,并且很可能降低整体性能。

插入数据行

  • 如果未调用 end()INSERT 会被中止。
  • 行会以 stream 的形式逐步发送,以分散网络负载。
  • 只有当所有行都位于同一分区,且行数小于 max_insert_block_size 时,ClickHouse 才会以原子方式插入批次。

异步插入 (服务端批处理)

你可以使用 ClickHouse 异步插入 来避免在客户端对传入数据进行批处理。只需将 async_insert 选项传给 insert 方法 (甚至可以直接设置在 Client 实例上,这样会影响所有 insert 调用) 即可。
另请参阅:

Inserter 功能 (客户端批处理)

需要启用 inserter Cargo 功能。
  • 如果达到任一阈值 (max_bytesmax_rowsperiod) ,Inserter 会在 commit() 中结束当前进行中的插入。
  • 可以使用 with_period_bias 为结束活动 INSERT 之间的时间间隔引入偏移,从而避免并行 inserter 带来的负载尖峰。
  • Inserter::time_left() 可用于判断当前周期何时结束。如果你的 stream 很少产生条目,请再次调用 Inserter::commit() 以检查限制条件。
  • 时间阈值基于 quanta crate 实现,以提升 inserter 的性能。如果启用了 test-util,则不会使用它 (因此,在自定义测试中可通过 tokio::time::advance() 控制时间) 。
  • 两次 commit() 调用之间的所有行都会插入到同一条 INSERT 语句中。
如果你想终止/完成插入,别忘了执行 flush:

执行 DDL 语句

对于单节点部署,像这样执行 DDL 语句即可:
不过,对于带有负载均衡器的集群部署或 ClickHouse Cloud,建议使用 wait_end_of_query 选项,等待 DDL 在所有副本上全部生效。可以这样操作:

ClickHouse 设置

你可以使用 with_option 方法应用各种 ClickHouse 设置。例如:
query 外,这种方式同样适用于 insertinserter 方法;此外,也可以在 Client 实例上调用同一方法,为所有查询设置全局参数。

Query ID

使用 .with_option 可以设置 query_id 选项,以便在 ClickHouse 查询日志中识别查询。
除了 query 之外,它与 insertinserter 方法的用法类似。
如果你手动设置 query_id,请确保它是唯一的。为此,UUID 是不错的选择。
另请参阅:客户端仓库中的 query_id 示例

会话 ID

query_id 类似,您也可以设置 session_id,让这些语句在同一会话中执行。session_id 既可以在客户端级别进行全局设置,也可以针对每次 queryinsertinserter 调用单独设置。
在集群部署场景下,由于缺少 “粘性会话”,你需要连接到特定的集群节点,才能正确使用此功能;例如,轮询负载均衡器无法保证后续请求会由同一个 ClickHouse 节点处理。
另请参见:客户端仓库中的 session_id 示例

自定义 HTTP 请求头

如果你使用代理进行身份验证,或者需要传递自定义请求头,可以按如下方式操作:
另请参见:客户端仓库中的自定义 HTTP 请求头示例

自定义 HTTP 客户端

这有助于调整底层 HTTP 连接池的设置。
此示例依赖旧版 Hyper API,后续可能会调整。
另请参阅客户端仓库中的自定义 HTTP 客户端示例

数据类型

  • (U)Int(8|16|32|64|128) 可与对应的 (u|i)(8|16|32|64|128) 类型或基于它们的 newtype 相互映射。
  • Int256UInt256 目前不支持直接映射,但可以使用变通方案
  • Float(32|64) 可与对应的 f(32|64) 或基于它们的 newtype 相互映射。
  • Decimal(32|64|128) 可与对应的 i(32|64|128) 或基于它们的 newtype 相互映射。使用 fixnum 或其他有符号定点数实现会更方便。
  • Boolean 可与 bool 或基于它的 newtype 相互映射。
  • String 可与任意字符串或字节类型相互映射,例如 &str&[u8]StringVec<u8>SmartString。也支持 newtype。若要存储字节,建议使用 serde_bytes,因为效率更高。
  • 支持将 FixedString(N) 作为字节数组使用,例如 [u8; N]
  • UUID 通过 serde::uuiduuid::Uuid 相互映射。需要启用 uuid feature。
  • Date 可映射为/从 u16 或其外层包装的 newtype,并表示自 1970-01-01 起经过的天数。此外,还支持 time::Date,可通过使用 serde::time::date 实现,但这需要启用 time feature。
  • Date32 可映射为/自 i32 或其外层的 newtype,表示自 1970-01-01 起经过的天数。此外,还支持通过 serde::time::date32 使用 time::Date,这需要启用 time feature。
  • DateTime 可映射为/自 u32 或其外层封装的 newtype,表示自 UNIX 纪元以来经过的秒数。此外,还支持 time::OffsetDateTime,可通过 serde::time::datetime 使用,但这需要启用 time feature。
  • DateTime64(_) 可与 i32 或其外层包装的 newtype 相互映射,表示自 Unix epoch 起经过的时间。此外,还支持通过 serde::time::datetime64::* 使用 time::OffsetDateTime,这需要启用 time feature。
  • Tuple(A, B, ...) 可映射为/从 (A, B, ...),或映射为/从基于它的 newtype
  • Array(_) 可映射为/从任意 slice,例如 Vec<_>&[_]。也支持自定义新类型。
  • Map(K, V) 的行为类似于 Array((K, V))
  • LowCardinality(_) 可无缝支持。
  • Nullable(_) 可映射为/从 Option<_>。对于 clickhouse::serde::* helpers,请添加 ::option
  • 可通过提供多个重命名后的数组来支持 Nested 类型。
  • 支持 Geo 类型。Point 的行为类似于 Tuple (f64, f64),其余类型都只是由点组成的切片。
  • VariantDynamic、 (新的) JSON 数据类型暂不支持。

模拟

该 crate 提供了一些工具,用于模拟 CH 服务器,以及测试 DDL、SELECTINSERTWATCH 查询。可通过 test-util feature 启用此功能。应将其用作开发依赖项。 参见该示例

故障排查

CANNOT_READ_ALL_DATA

CANNOT_READ_ALL_DATA 错误最常见的原因是,应用程序端的行定义与 ClickHouse 中的定义不一致。 考虑以下表:
然后,如果应用端定义的 EventLog 类型不匹配,例如:
插入数据时,可能会出现以下错误:
在此示例中,可通过正确定义 EventLog struct 来修复此问题:

已知限制

  • 目前尚不支持 VariantDynamic 和 (新的) JSON 数据类型。
  • 目前尚不支持服务器端参数绑定;跟踪进展请参见此 issue

联系我们

如果你有任何问题或需要帮助,欢迎通过 Community SlackGitHub issues 与我们联系。
最后修改于 2026年7月23日