跳转到主要内容
当客户端侧批处理不可行时,ClickHouse 的异步插入提供了一种强有力的替代方案。这在可观测性工作负载中尤其有价值,因为成百上千个 agent 会持续发送数据——日志、指标、链路追踪——而且这些数据通常都是小型、实时的载荷。在这类环境中,在客户端侧缓冲数据会增加复杂性,因为需要集中式队列来确保能够发送足够大的批次。
不建议在同步模式下发送大量小批次,这会导致创建许多 parts,进而造成查询性能下降,并引发”too many part”错误。
异步插入会先将传入数据写入内存缓冲区,再根据可配置的阈值将其刷写到存储,从而把批处理责任从客户端转移到服务器端。这种方式可显著减少 parts 创建开销、降低 CPU 使用率,并确保摄取即使在高并发下也能保持高效。 其核心行为由 async_insert 设置控制。 异步插入同时支持 HTTP 和原生 TCP 接口。 启用后 (async_insert = 1) ,插入操作会先被缓冲,只有在满足以下某个刷写条件时才会写入磁盘: 哪个阈值先达到,就会触发刷写。 这一批处理过程对客户端是透明的,并帮助 ClickHouse 高效合并来自多个来源的插入流量。不过,在发生刷写之前,这些数据无法被查询。需要特别注意的是,针对每种插入形态与设置组合,都会有多个缓冲区;而在集群中,缓冲区则按节点分别维护——从而能够在多租户环境中实现细粒度控制。除此之外,插入机制与同步插入中描述的内容完全相同。

选择返回模式

可以通过 wait_for_async_insert 设置进一步细化异步插入的行为。 当该值设为 1 (默认值) 时,ClickHouse 只有在数据成功写入磁盘后才会确认插入。这样可以提供更强的持久性保障,也让错误处理更直接:如果在刷写期间发生问题,错误会返回给客户端。对于大多数生产场景,尤其是在必须可靠跟踪插入失败时,建议使用此模式。 基准测试 表明,得益于自适应插入以及稳定的 part 创建行为,它在并发场景下也能很好地扩展——无论是运行 200 个还是 500 个客户端。 wait_for_async_insert = 0 设为 0 会启用“发出即忘”模式。在这种模式下,server 会在数据进入缓冲区后立即确认插入,而不会等待其写入存储。 这种方式可实现超低延迟插入和最高吞吐量,非常适合高速度、低关键性的数据场景。但这也有代价:无法保证数据一定会持久化,错误只会在刷写期间暴露,而且失败的插入也没有 dead-letter queue——要追踪失败,只能事后检查服务器日志和系统表。只有在你的 workload 可以容忍数据丢失时,才应使用此模式。 基准测试还表明,当缓冲区刷写不频繁时 (例如每 30 秒一次) ,part 数量会显著减少,CPU 使用率也会更低,但静默失败的风险依然存在。 我们的强烈建议是:如果使用异步插入,请使用 async_insert=1,wait_for_async_insert=1。使用 wait_for_async_insert=0 风险很高,因为即使发生错误,你的 INSERT 客户端也可能毫不知情;此外,如果客户端在 ClickHouse server 需要降低写入速度并施加反压以确保服务可靠性时仍持续快速写入,还可能导致潜在过载。

自适应异步插入

从 24.2 版本开始,ClickHouse 默认使用自适应刷写超时 (async_insert_use_adaptive_busy_timeout) 。不再使用固定的刷写间隔,而是根据传入数据速率,在最小值 (async_insert_busy_timeout_min_ms,默认 50 毫秒) 和最大值 (async_insert_busy_timeout_max_ms,默认 200 毫秒,Cloud 上为 1000 毫秒) 之间动态调整超时。 当数据频繁到达时,超时会保持更接近最小值,以便更早刷写并降低端到端延迟。当数据较为稀疏时,超时则会增大并趋近最大值,以积累更大的批次。这在默认模式 (wait_for_async_insert=1) 下尤其有用,因为如果使用固定且较高的超时,即使数据已经可以刷写,客户端仍需阻塞等待整个时间间隔结束。

错误处理

Schema 验证和数据解析发生在缓冲区 刷写 时,而不是在收到插入时进行。如果某个插入查询中的任意一行出现解析错误或类型错误,该查询中的所有数据都不会被 刷写——整个查询的载荷都会被拒绝。在默认模式 (wait_for_async_insert=1) 下,错误会返回给客户端。在发出即忘模式下,错误会被写入服务器日志和 system.asynchronous_inserts 表。 每次 刷写 都会针对缓冲区中每个不同的分区键值至少创建一个 part。即使对于没有分区键的表,如果缓冲数据超过 max_insert_block_size (默认约 100 万行) ,单次 刷写 也可能生成多个 parts。
即使使用了异步插入,如果分区键的基数较高,你仍然可能遇到”parts 过多”错误。

去重与可靠性

默认情况下,ClickHouse 会对同步插入自动去重,因此在发生故障时进行重试也是安全的。不过,对异步插入而言,除非显式启用,否则该功能默认关闭 (如果你有依赖它的 materialized view,则不应启用——参见 issue) 。 在实际使用中,如果开启了去重,并且同一次插入因超时或网络中断等原因被重试,ClickHouse 就可以安全地忽略重复数据。这有助于保持幂等性,避免数据被重复写入。

启用异步插入

可以为特定用户或特定查询启用异步插入:
  • 在用户级别启用异步插入。此示例使用用户 default;如果你创建了其他用户,请将其替换为相应的用户名:
    ALTER USER default SETTINGS async_insert = 1
    
  • 你可以通过插入查询的 SETTINGS 子句指定异步插入设置:
    INSERT INTO YourTable SETTINGS async_insert=1, wait_for_async_insert=1 VALUES (...)
    
  • 使用 ClickHouse 编程语言客户端时,也可以将异步插入设置指定为连接参数。 例如,使用 ClickHouse Java JDBC 驱动连接到 ClickHouse Cloud 时,可以在 JDBC 连接字符串中这样设置:
    "jdbc:ch://HOST.clickhouse.cloud:8443/?user=default&password=PASSWORD&ssl=true&custom_http_params=async_insert=1,wait_for_async_insert=1"
    
异步插入不适用于 INSERT INTO ... SELECT 查询。当插入包含 SELECT 子句时,无论 async_insert 设置如何,查询始终都会同步执行。

关闭时刷新缓冲区

如需刷新所有待处理的 async insert 缓冲区 (例如在优雅关闭期间或维护前) ,请运行:
SYSTEM FLUSH ASYNC INSERT QUEUE
这可确保在服务器停止前,所有缓冲的数据都会先写入存储。

与缓冲表的比较

异步插入是 缓冲表 的现代替代方案。主要区别如下:
  • 无需修改 DDL。 异步插入对用户是透明的——你只需启用一个设置,无需创建额外的表。
  • 按形态缓冲。 异步插入会针对每种唯一的查询形态和设置组合维护单独的缓冲区,从而实现更细粒度的刷写策略。缓冲表则是每个目标表只有一个缓冲区。
  • 持久性。 在默认模式 (wait_for_async_insert=1) 下,客户端收到确认之前,数据就已写入磁盘。缓冲表的行为类似“发出即忘”——如果发生崩溃,缓冲中的数据会丢失。
  • 集群行为。 在集群中,异步插入缓冲区按节点分别维护。缓冲表则需要在每个节点上显式创建。
最后修改于 2026年6月10日