Skip to main content

概述

ClickHouse 支持 Apache Arrow Flight 协议——这是一种高性能 RPC 框架,可通过 gRPC 使用 Arrow IPC 格式高效传输列式数据。 该实现还支持 Arrow Flight SQL,使支持 Flight SQL 协议的 BI 工具和应用程序能够直接查询 ClickHouse。 主要功能:
  • 执行 SQL 查询,并以 Apache Arrow 格式返回结果。
  • 使用 Arrow 格式向表中插入数据。
  • 通过 Flight SQL 命令查询元数据 (目录、schema、表、主键) 。
  • 通过 Flight SQL 创建、绑定、执行和关闭服务器端预处理语句。
  • 通过 Flight SQL 操作管理会话和设置。
  • 支持 TLS 加密以及用户名/密码身份验证。
  • 通过 PollFlightInfo 增量获取结果。
  • 通过 CancelFlightInfo 取消查询。

启用 Arrow Flight Server

要启用 Arrow Flight Server,请将 arrowflight_port 设置添加到 ClickHouse server 配置中:
启动时,日志中会显示一条消息,确认该接口已启用:

TLS 配置

要为 Arrow Flight 接口启用 TLS,请按如下方式进行配置:
启用 TLS 时,客户端必须使用 grpc+tls:// 协议进行连接,而不能使用 grpc://。

身份验证

Arrow Flight 接口支持以下两种身份验证方法:

基本身份验证

客户端通过标准 HTTP Authorization: Basic 请求头,使用用户名和密码进行身份验证。身份验证成功后,服务器会在响应头中返回一个 Bearer 令牌。

Bearer 令牌身份验证

后续请求可通过 Authorization: Bearer <token> 请求头,使用 Basic 身份验证返回的 Bearer 令牌。该令牌每次使用时都会自动刷新,并会根据 default_session_timeout 服务器设置过期 (默认值:60 秒) 。

Python 示例

启用 TLS:

会话管理

Arrow Flight 接口通过自定义 gRPC 元数据请求头支持 ClickHouse 会话:
由于 Arrow Flight 使用基于 HTTP/2 的 gRPC,元数据请求头名称区分大小写,且必须完全按如下所示使用小写形式 (例如 x-clickhouse-session-id,而不是 X-ClickHouse-Session-Id) 。这是 RFC 9113 第 8.2 节的要求;该规范规定 HTTP/2 字段名称只能包含小写字符。这与 HTTP/1.1 不同,因为在 HTTP/1.1 中,请求头名称不区分大小写。
会话支持通过 SetSessionOptions 操作持久化设置 ClickHouse 设置 (参见 DoAction) 。

服务器配置参考

支持的 RPC 方法

GetFlightInfo

执行查询并返回一个 FlightInfo,其中包含结果 schema、用于检索数据的带 ticket 的端点、行数以及字节数。 接受一个 FlightDescriptor,其可以是:
  • PATH 描述符:仅包含一个组件的 path,会被解释为表名。会生成 SELECT * FROM <table>。
  • CMD 描述符:原始 SQL 查询字符串,或序列化后的 Flight SQL protobuf 命令 (参见 Flight SQL Commands) 。
查询会被完整执行,结果存储在服务器端 ticket 中。每个数据块都会生成一个独立的端点/ticket,使客户端能够并行检索数据。

PollFlightInfo

支持对长时间运行的查询进行增量式结果获取。与 GetFlightInfo 需要等待整个查询完成不同,PollFlightInfo 会按块返回结果。 首次调用时,查询会开始执行。响应包括:
  • 一个 FlightInfo,其中包含截至当前所有可用数据块的端点。
  • 一个用于下一次轮询的 FlightDescriptor (如果预计还会有更多结果) 。
后续使用返回的描述符调用时,会获取更多块。当没有更多数据可用时,响应中将不再包含下一个描述符。
当前实现会阻塞,直到有数据块可用,而不是在没有数据时立即返回。

GetSchema

返回查询结果的 Arrow schema,而无需执行完整查询。接受与 GetFlightInfo 相同的描述符类型。

DoGet

检索给定 ticket 对应的数据。接受以下任一形式:
  • 由 GetFlightInfo 或 PollFlightInfo 返回的 ticket。
  • 作为 ticket 值提供的原始 SQL 查询字符串。

DoPut

将数据发送至 ClickHouse。接收一个 FlightDescriptor 和 Arrow 记录批次流。 按表名插入 (PATH 描述符) :
使用 SQL 插入 (CMD 描述符) :
通过 Flight SQL CommandStatementUpdate 执行 DDL/DML: Flight SQL 客户端使用 CommandStatementUpdate 执行 DDL/DML 语句 (CREATE、INSERT、ALTER 等) 。响应中会包含受影响的行数。 通过 Flight SQL CommandStatementIngest 进行批量摄取: 仅支持向现有表追加数据 (TABLE_NOT_EXIST_OPTION_FAIL + TABLE_EXISTS_OPTION_APPEND) 。此命令不支持目录和临时表。 transaction_id 不受 CommandStatementUpdate 或 CommandStatementIngest 支持。如果提供,ClickHouse 会返回 NotImplemented 错误。
数据传输仅接受 Arrow 格式。在 SQL 中指定其他格式 (例如 FORMAT JSON) 会导致错误。

DoAction

执行命名操作。支持以下操作:

CancelFlightInfo

取消与某个 FlightInfo 关联的正在执行的查询。查询 ID 从 FlightInfo 的 app_metadata 字段中提取。还会取消与该查询关联的所有轮询描述符。

SetSessionOptions

为当前会话设置 ClickHouse 服务器级设置。要求通过 x-clickhouse-session-id 请求头指定会话 ID。 支持的值类型:string、boolean、integer、double 以及字符串列表。 如果设置名称未知,则返回错误 INVALID_NAME。如果值无法解析,则返回错误 INVALID_VALUE。

GetSessionOptions

返回当前会话的所有 ClickHouse 设置及其值。返回一个从设置名称到字符串值的映射 (内部会查询 system.settings) 。

CreatePreparedStatement

创建服务器端预处理语句,并返回语句句柄。请求中包含带有 ? 占位符的 SQL 查询文本。 此操作不支持 transaction_id。如果提供了该参数,ClickHouse 会返回 NotImplemented 错误。 对于查询语句,响应可能包括:
  • dataset_schema:结果集的 schema。
  • parameter_schema:语句参数的 schema。
如果对有效查询进行 schema 推断失败 (例如,将占位符替换为 NULL 对该查询无效时) ,ClickHouse 仍会创建预处理语句,并返回不包含 dataset_schema 的句柄。 正如 Flight SQL 规范所预期的那样,dataset_schema 只是一个尽力而为的猜测——规范指出,结果 schema 可能取决于参数,服务器应给出其最佳猜测,且客户端不得假定该 schema 是准确的。请不要依赖它;应执行语句以获取描述实际数据的 schema。在 ClickHouse 中,它可能与实际返回的内容不同,原因有两个:
  • 推断会将每个 ? 替换为 NULL,因此决定某个结果列的占位符,其类型来自该 NULL,而不是你之后绑定的值。SELECT ? AS x 会推断出类型为 Nothing 的列,但绑定 5 时实际返回的是 UInt8。仅用于 predicate 的占位符,例如 SELECT id, name FROM t WHERE id = ?,则不存在此问题,因为结果类型来自表。
  • 没有 Arrow 等价类型的列,其 Arrow 类型取自 output_format_arrow_unsupported_types,而每次调用都会根据发起该调用的会话来解析该设置。由于句柄归属于用户而非某个单独的会话,后续调用可能会以不同方式解析它,从而在声明为 utf8 时返回 binary,或者相反。在预处理查询本身中设置该模式,即可为两者固定其取值。
预处理语句归已通过身份验证的用户所有,而不归属于某个单独的会话。如果你以同一用户身份打开多个会话,则可以在其中任意一个会话中执行、重新绑定和关闭同一个语句句柄。 其他用户不能执行、绑定或关闭不是由自己创建的语句句柄。 arrowflight.prepared_statements_lifetime_seconds 用于控制过期行为:
  • > 0:使用配置值作为语句的生命周期。对于绑定到会话和无会话的语句,每次请求都会刷新过期时间。
  • 0:预处理语句不会自动过期。
  • -1 (默认) :如果语句是在会话中创建的,其生命周期遵循该会话的 timeout,并在该会话中的每次请求时刷新。如果语句是在没有会话的情况下创建的,则不会自动过期。
已过期的语句会被移除,并且不再计入 arrowflight.max_prepared_statements_per_user。

ClosePreparedStatement

当请求包含非空的 statement handle 时,会关闭一个预处理语句,并释放相关的服务端资源。 当 handle 为空时,ClickHouse 也支持使用 ClosePreparedStatement 进行批量关闭:
  • 如果存在 x-clickhouse-session-id,则会关闭该 session 中该已认证用户的所有预处理语句。
  • 如果不存在 session ID,则只会关闭该已认证用户未绑定到任何 session 的预处理语句。
如果某个预处理语句是在某个 session 中创建的 (通过 x-clickhouse-session-id) ,那么当该 session 关闭时,该语句也会自动关闭。

Flight SQL 命令

当 CMD 描述符中包含序列化后的 Flight SQL protobuf 消息时,ClickHouse 会处理以下命令:

通过 GetFlightInfo / GetSchema 支持的命令

通过 DoPut 支持

ClickHouse 中不支持的功能

这些命令对应的是 ClickHouse 不提供的功能,因此 Arrow Flight SQL 接口不支持。

完整示例

Query
Response

数据格式

所有数据均以 Apache Arrow IPC 格式传输。仅支持 Arrow 格式——指定其他 ClickHouse 格式 (例如 FORMAT JSON、FORMAT CSV) 会导致报错。 序列化过程中,ClickHouse 数据类型会映射为 Arrow 类型。Arrow Flight 始终使用规范的 Arrow 映射,并且与 Arrow 和 ArrowStream 输出格式不同,它不遵循那些改变类型表示方式的 output_format_arrow_* 设置——output_format_arrow_string_as_string、output_format_arrow_low_cardinality_as_dictionary、output_format_arrow_date_as_uint16、output_format_arrow_fixed_string_as_fixed_byte_array 以及字典索引相关设置在此处均不起作用。因此,同一个查询通过 Arrow Flight 得到的 schema 可能与通过 FORMAT Arrow 得到的不同,这是有意的设计,原因有两点:
  • Flight SQL 固定了其元数据响应的 schema。例如,CommandGetTables 必须返回 catalog_name: utf8, db_schema_name: utf8, table_name: utf8 not null, table_type: utf8 not null, table_schema: bytes not null。如果允许某个 session setting 把这些 utf8 列变成 binary,那么对所有 Flight SQL 驱动而言 ClickHouse 都将不符合规范,同时还会改变 ClickHouse 在 table_schema 中声明的每个表的 schema。
  • Flight 客户端通过不同的调用分别获取 schema 和数据 (先是 GetFlightInfo 或 GetSchema,然后是 DoGet) 。只要某个设置能够改变 schema,就存在这样的可能:两次调用之间 session 发生变化,导致所声明的 schema 与实际传输的 stream 不一致。
唯一的例外是完全没有 Arrow 对应类型的类型,例如 JSON、Dynamic、QBit 或 AggregateFunction。由于没有可遵循的规范映射,ClickHouse 必须自行选择一种表示方式,而 output_format_arrow_unsupported_types 让你决定采用哪一种: AggregateFunction 列是唯一即使在 text 模式下仍保持为 Arrow Binary 列的类型:它的 text form 是原始的 aggregate state,并非 valid UTF-8,而 Arrow Utf8 列必须存放 valid UTF-8。如果你需要可读的值,请使用 finalizeAggregation。 出于同样的原因,ClickHouse 在把 text 值写入 Utf8 列之前,会将其中每个 invalid UTF-8 sequence 替换为 U+FFFD (�) 。持有 String 的 Dynamic 会原样序列化这些字节,而这些字节可能是任意内容,因此若不做替换,该列就会违反 Arrow 规范,并可能被严格的客户端拒绝。只有本来就不是有效文本的值才会发生变化。在必须精确保留字节的场景下,请使用 binary 模式。 output_format_arrow_string_as_string 对这些列从不生效,在 FORMAT Arrow 中同样如此——它只作用于真正的 String 和 FixedString 列。因此,clickhouse.opaque 列的 Arrow 类型总能表明它所承载的 encoding:Utf8 表示 text form,Binary 表示 binary form。 这就是为什么保存在 Dynamic 中的 aggregate state 在 text 模式下会丢失信息,而 AggregateFunction 列却不会。该列的类型来自 Dynamic,而 Dynamic 并不说明其各行承载什么内容,且 schema 在看到任何值之前就已确定,因此无法为该 state 单独分配一个 Binary 列。若要保留它,请使用 binary 模式。Variant 会列出其所有可选类型,因此其中的 AggregateFunction 会获得自己的 Binary 子列,不受影响。 除此之外,这样的列与真正的 Utf8/Binary 列无法区分,因此它被声明为 Arrow extension 类型:字段元数据中 ARROW:extension:name = clickhouse.opaque,并在 ARROW:extension:metadata 中保存原始的 ClickHouse 类型名。不识别该 extension 名称的客户端会看到底层的 plain storage 类型,这符合 Arrow 规范的规定。Nested columns 在各自的字段上打标签,因此带标签的是 Array(JSON) 的子字段,以及 Map(JSON, ...) 的键,而不是容器本身。 旧的布尔值设置 output_format_arrow_unsupported_types_as_binary 仍然有效:取值为 0 时等同于 throw,取值为 1 时等同于 binary。仅当 output_format_arrow_unsupported_types 保持默认值时,才会参考该设置。

兼容性

Arrow Flight 接口兼容任何支持 Arrow Flight 或 Arrow Flight SQL 协议的客户端或工具,包括:
  • Python (pyarrow)
  • Java (org.apache.arrow.flight)
  • C++ (arrow::flight)
  • Go (apache/arrow/go)
  • ADBC (Arrow Database Connectivity) 驱动
  • DBeaver,以及其他支持 Flight SQL 的工具
如果你所用的工具有原生 ClickHouse 连接器可用 (例如 JDBC、ODBC、native protocol) ,那么除非出于性能或格式兼容性的特定需求必须使用 Arrow Flight,否则应优先使用该连接器。

客户端 ArrowFlight 特性

ClickHouse 也可以作为 Flight 客户端,从外部 Arrow Flight 服务器读取数据。参见:

另请参阅

最后修改于 2026年9月26日