选择插入策略
高效的数据摄取构成了高性能 ClickHouse 部署的基础。选择正确的插入策略可以显著影响吞吐量、成本和可靠性。本节概述了最佳实践、权衡和配置选项,以帮助您为工作负载做出正确的决策。
以下假设您通过客户端将数据推送到 ClickHouse。如果您是通过内置表函数(如 s3 和 gcs)将数据拉入 ClickHouse,我们推荐您查看我们的指南 "优化 S3 插入和读取性能"。
默认的同步插入
默认情况下,对 ClickHouse 的插入是同步的。每个插入查询会立即在磁盘上创建一个存储部分,包括元数据和索引。
如果不能,请参见下面的 异步插入。
我们简要回顾 ClickHouse 的 MergeTree 插入机制:

客户端步骤
为了实现最佳性能,数据必须 ① 批处理,批量大小是 第一个决定。
ClickHouse 会根据表的主键列按顺序 存储插入的数据。 第二个决定 是在传输到服务器之前是否 ② 对数据进行预排序。如果批量数据按主键列预排序到达,ClickHouse 可以 跳过 ⑨ 排序步骤,从而加快摄取速度。
如果要摄取的数据没有预定义格式, 关键决策 是选择格式。ClickHouse 支持以 70 多种格式 插入数据。然而,当使用 ClickHouse 命令行客户端或编程语言客户端时,通常会自动处理此选择。如有需要,此自动选择也可以显式覆盖。
下一个 主要决策 是 ④ 是否在传输到 ClickHouse 服务器之前压缩数据。压缩可以减少传输大小并提高网络效率,从而加快数据传输并降低带宽使用,尤其是对于大型数据集。
数据会 ⑤ 传输到 ClickHouse 网络接口——可以是 native 或 HTTP 接口(稍后我们将对此进行比较)。
服务器步骤
在 ⑥ 接收数据后,如果使用了压缩,ClickHouse 会 ⑦ 解压缩数据,然后 ⑧ 从原始发送格式解析数据。
使用该格式化数据的值和目标表的 DDL 语句,ClickHouse ⑨ 构建一个内存中的 块,以 MergeTree 格式 ⑩ 按主键列排序,如果它们尚未预排序,⑪ 创建一个 稀疏主索引,⑫ 应用 按列压缩,并将数据 ⑬ 作为新的 ⑭ 数据部分 写入磁盘。
如果是同步插入则采用批量插入
上述机制说明无论插入大小如何,总是存在恒定的开销,使得批量大小成为提升摄取吞吐量的最重要优化因素。批量插入减少了作为总插入时间比例的开销,提高了处理效率。
我们建议以至少1,000行的批量插入数据,理想情况下为10,000到100,000行之间。较少的、大规模的插入减少了写入的分区片段数量,最小化了合并负载,并降低了整体系统资源使用。
要使同步插入策略有效,需要在客户端进行批量处理。
如果您无法在客户端进行数据批量处理,ClickHouse支持异步插入,将批量处理转移到服务器(请参见)。
无论插入的大小如何,我们建议将插入查询的数量保持在每秒大约一个插入查询。推荐的原因是创建的分区片段在后台合并为更大的分区片段(以优化您的数据以便于读取查询),每秒发送过多的插入查询可能会导致后台合并无法跟上新分区片段的数量。然而,当您使用异步插入时(见异步插入),可以使用更高的每秒插入查询速率。
确保幂等重试
同步插入也是 幂等的。在使用 MergeTree 引擎时,ClickHouse 默认会去重插入。这可以防止模糊的失败情况,例如:
- 插入成功但由于网络中断客户端从未收到确认。
- 插入在服务器端失败并超时。
在这两种情况下,安全地 重试插入 是可以的——只要批量内容和顺序保持不变。因此,客户端的一致重试至关重要,不能修改或重新排序数据。
选择正确的插入目标
对于分片集群,您有两种选择:
- 直接插入到 MergeTree 或 ReplicatedMergeTree 表中。这是最有效的选项,当客户端可以在分片之间进行负载均衡时。启用
internal_replication = true
后,ClickHouse 透明地处理复制。 - 插入到 分布式表 中。这允许客户端将数据发送到任何节点,并让 ClickHouse 转发到正确的分片。这比较简单,但由于额外的转发步骤,性能略差。仍然建议启用
internal_replication = true
。
在 ClickHouse Cloud 中,所有节点都对同一个分片进行读写。插入会自动在节点之间进行平衡。用户只需将插入发送到暴露的端点。
选择正确的格式
选择正确的输入格式对于 ClickHouse 中高效的数据摄取至关重要。支持70多种格式时,选择最有效的选项可以显著影响插入速度、CPU 和内存使用率以及整体系统效率。
虽然灵活性对数据工程和基于文件的导入是有用的,应用程序应优先考虑以性能为导向的格式:
- Native 格式(推荐):最高效。列式,服务器端需要最小解析。Go 和 Python 客户端默认使用。
- RowBinary:高效的行格式,理想情况下在客户端难以进行列式转换。用于 Java 客户端。
- JSONEachRow:易于使用但解析成本高。适合于低数据量的用例或快速集成。
使用压缩
压缩在减少网络开销、加快插入速度和降低 ClickHouse 存储成本方面起着关键作用。有效使用时,它增强了摄取性能,而无需更改数据格式或架构。
压缩插入数据会减少通过网络发送的有效负载大小,最小化带宽使用并加速传输。
对于插入,压缩在与 Native 格式一起使用时尤其有效,后者已经与 ClickHouse 的内部列式存储模型匹配。在这种设置中,服务器可以高效解压缩并直接存储数据,几乎无需转化。
在速度上使用 LZ4,在压缩率上使用 ZSTD
ClickHouse 在数据传输过程中支持多种压缩编解码器。两个常见的选项是:
- LZ4:快速且轻量。它显著减少数据大小,同时 CPU 开销最小,非常适合高吞吐量插入,是大多数 ClickHouse 客户端的默认选项。
- ZSTD:更高的压缩比,但 CPU 消耗更多。在网络传输成本较高时(例如在跨区域或云提供商场景中)很有用,尽管它会稍微增加客户端计算和服务器端解压缩时间。
最佳实践:使用 LZ4,除非带宽受限或产生数据外发费用——那时考虑使用 ZSTD。
在 FastFormats 基准 的测试中,LZ4 压缩的 Native 插入将数据大小减少了超过 50%,将 5.6 GiB 数据集的摄取时间从 150 秒缩短至 131 秒。切换到 ZSTD 将相同数据集压缩至 1.69 GiB,但略微增加了服务器端处理时间。
压缩减少资源使用
压缩不仅减少了网络流量——它还提高了服务器的 CPU 和内存效率。通过压缩数据,ClickHouse 接收的字节更少,并花费更少的时间解析大量输入。在多个并发客户端(例如可观察性场景)摄取时,这种好处尤为重要。
对于 LZ4,压缩对 CPU 和内存的影响温和,而对于 ZSTD 的影响适中。即使在负载下,由于数据量减少,服务器端效率也会提高。
将压缩与批处理和高效输入格式(如 Native)结合使用,能获得最佳的摄取性能。
当使用本机接口(例如 clickhouse-client)时,默认启用 LZ4 压缩。您可以通过设置选项切换到 ZSTD。
在 HTTP 接口 中,使用 Content-Encoding 头应用压缩(例如 Content-Encoding: lz4)。整个有效负载必须在发送前进行压缩。
如果低成本则进行预排序
在插入之前按主键对数据进行预排序可以改善 ClickHouse 的摄取效率,特别是对于大批量数据。
当数据预排序到达时,ClickHouse 可以在创建分区时跳过或简化内部排序步骤,从而减少 CPU 使用率并加快插入过程。预排序还提高了压缩效率,因为类似的值会聚集在一起——使得像 LZ4 或 ZSTD 这样的编码器能够实现更好的压缩比。在与大批量插入和压缩结合使用时,这种好处尤其明显,因为它减少了处理开销和传输的数据量。
也就是说,预排序是可选的优化——不是必需的。 ClickHouse 可以高效地使用并行处理对数据进行排序,在许多情况下,服务器端排序比客户端预排序的速度更快或更方便。
我们建议仅在数据几乎已排序或客户端资源(CPU、内存)充足且未充分利用时进行预排序。 在延迟敏感或高吞吐量的用例中,例如可观察性,在数据无序到达或来自许多代理的情况下,通常最好跳过预排序,依赖 ClickHouse 的内置性能。
异步插入
在 ClickHouse 中,异步插入提供了一种强大的替代方案,当客户端批处理不可行时。尤其是在可观察性工作负载中,数百或数千个代理持续发送数据 — 日志、指标、跟踪 — 通常以小的实时有效载荷进行发送。在这些环境中,客户端缓冲数据增加了复杂性,需要一个集中队列以确保可以发送足够大的批次。
不推荐以同步模式发送许多小批次,这将导致创建多个部分。这将导致查询性能不佳以及出现 "too many part" 错误。
异步插入通过将传入的数据写入内存中的缓冲区,然后根据可配置的阈值将其刷新到存储,从而将批处理责任从客户端转移到服务器。这种方法显著减少了部分创建的开销,降低了 CPU 的使用,并确保在高并发下的摄取效率。
核心行为通过 async_insert
设置进行控制。

当启用时 (1),插入将被缓冲,并且仅在满足以下任何一个刷新条件后写入磁盘:
(1) 缓冲区达到指定的大小 (async_insert_max_data_size) (2) 时间阈值经过 (async_insert_busy_timeout_ms) 或 (3) 累积到达最大插入查询数量 (async_insert_max_query_number)。
该批处理过程对客户端是不可见的,并帮助 ClickHouse 高效地合并来自多个源的插入流量。然而,在刷新发生之前,数据无法被查询。重要的是,每种插入形状和设置组合有多个缓冲区,并且在集群中,缓冲区是每个节点维护的 - 这允许在多租户环境中进行精细控制。插入机制在其他方面与 同步插入 描述的相同。
选择返回模式
异步插入的行为通过 wait_for_async_insert
设置进一步细化。
当设置为 1 (默认值) 时,ClickHouse 仅在数据成功刷新到磁盘后才确认插入。这确保了强大的持久性保证,使错误处理变得简单:如果在刷新时出现问题,错误将返回给客户端。此模式推荐用于大多数生产场景,特别是当插入失败需要可靠跟踪时。
基准测试 显示它在并发上表现良好 - 无论您是在运行 200 还是 500 个客户端 - 这得益于自适应插入和稳定的部分创建行为。
设置 wait_for_async_insert = 0
启用“无视而过”模式。在这里,服务器在数据被缓冲后立即确认插入,而无需等待数据达到存储。
这提供了超低延迟的插入和最大的吞吐量,理想用于高速度、低关键性的数据。然而,这也带来了权衡:无法保证数据被持久化,错误可能仅在刷新时显现,并且很难追踪失败的插入。只有在工作负载可以容忍数据丢失的情况下才使用此模式。
基准测试还表明 当缓冲刷新不频繁时(例如每 30 秒),部分数量显著减少,并且 CPU 使用率降低,但沉默失败的风险仍然存在。
我们强烈建议使用 async_insert=1,wait_for_async_insert=1
,如果使用异步插入。使用 wait_for_async_insert=0
非常冒险,因为您的 INSERT 客户端可能无法意识到是否存在错误,并且可能会导致潜在的过载,如果您的客户端在 ClickHouse 服务器需要减缓写入速度并产生一些回压以确保服务可靠性时继续快速写入。
去重和可靠性
默认情况下,ClickHouse 为同步插入执行自动去重,这使得在失败场景中重试是安全的。然而,这在异步插入中是禁用的,除非明确启用(如果您有依赖的物化视图,则不应启用 - 参见问题)。
实际上,如果去重启用并且由于超时或网络中断而重新尝试相同插入,ClickHouse 可以安全地忽略重复。这有助于保持幂等性并避免重复写入数据。仍然值得注意的是,插入验证和模式解析仅在缓冲刷新期间发生 - 因此错误(如类型不匹配)仅在那时浮现。
启用异步插入
可以为特定用户或特定查询启用异步插入:
- 在用户级别启用异步插入。此示例使用用户
default
,如果您创建了不同的用户,请替换该用户名:
- 您可以通过在插入查询中使用 SETTINGS 子句来指定异步插入设置:
- 在使用 ClickHouse 编程语言客户端时,您还可以将异步插入设置指定为连接参数。
作为一个示例,这就是在使用 ClickHouse Java JDBC 驱动程序连接到 ClickHouse Cloud 时,您可以在 JDBC 连接字符串中这样做:
选择一个接口 - HTTP 或 Native
Native
ClickHouse 提供了两种主要的数据摄取接口:native 接口和 HTTP 接口,两者在性能和灵活性之间存在权衡。native 接口由 clickhouse-client 和选定语言的客户端(如 Go 和 C++)使用,专为性能而构建。它始终以 ClickHouse 高效的 Native 格式传输数据,支持使用 LZ4 或 ZSTD 进行块级压缩,并通过将解析和格式转换等工作卸载到客户端来最小化服务器端处理。
它甚至使得可以在客户端计算 MATERIALIZED 和 DEFAULT 列的值,从而使服务器能够完全跳过这些步骤。这使得 native 接口非常适合高吞吐量的插入场景,其中效率至关重要。
HTTP
与许多传统数据库不同,ClickHouse 还支持 HTTP 接口。这相比之下,优先考虑兼容性和灵活性。 它允许数据以 任意支持的格式 发送——包括 JSON、CSV、Parquet 等,并在大多数 ClickHouse 客户端中广泛支持,包括 Python、Java、JavaScript 和 Rust。
这通常比 ClickHouse 的 native 协议更可取,因为它允许与负载均衡器轻松切换流量。我们预计使用 native 协议时,插入性能会有小差异,后者的开销更低。
然而,它缺乏 native 协议的深度集成,并且无法执行客户端优化,如计算物化值或自动转换为 Native 格式。虽然 HTTP 插入仍然可以使用标准 HTTP 头进行压缩(例如 Content-Encoding: lz4
),但压缩应用于整个有效负载,而不是单个数据块。该接口通常在协议简单性、负载均衡或广泛格式兼容性比原始性能更重要的环境中受到青睐。
有关这些接口的更详细描述,请参见 此处。