Selecting an insert strategy

- **Native format** (recommended): Most efficient. Column-oriented, minimal parsing required server-side. Used by default in Go and Python clients.
- **RowBinary**: Efficient row-based format, ideal if columnar transformation is hard client-side. Used by the Java client.
- **JSONEachRow**: Easy to use but expensive to parse. Suitable for low-volume use cases or quick integrations.

### Use compression[​](#use-compression "Direct link to Use compression")

Compression plays a critical role in reducing network overhead, speeding up inserts, and lowering storage costs in ClickHouse. Used effectively, it enhances ingestion performance without requiring changes to data format or schema.

Compressing insert data reduces the size of the payload sent over the network, minimizing bandwidth usage and accelerating transmission.

For inserts, compression is especially effective when used with the Native format, which already matches ClickHouse's internal columnar storage model. In this setup, the server can efficiently decompress and directly store the data with minimal transformation.

#### Use LZ4 for speed, ZSTD for compression ratio[​](#use-lz4-for-speed-zstd-for-compression-ratio "Direct link to Use LZ4 for speed, ZSTD for compression ratio")

ClickHouse supports several compression codecs during data transmission. Two common options are:

- **LZ4**: Fast and lightweight. It reduces data size significantly with minimal CPU overhead, making it ideal for high-throughput inserts and default in most ClickHouse clients.
- **ZSTD**: Higher compression ratio but more CPU-intensive. It's useful when network transfer costs are high—such as in cross-region or cloud provider scenarios—though it increases client-side compute and server-side decompression time slightly.

Best practice: Use LZ4 unless you have constrained bandwidth or incur data egress costs — then consider ZSTD.

Note

In tests from the [FastFormats benchmark](https://clickhouse.com/blog/clickhouse-input-format-matchup-which-is-fastest-most-efficient), LZ4-compressed Native inserts reduced data size by more than 50%, cutting ingestion time from 150s to 131s for a 5.6 GiB dataset. Switching to ZSTD compressed the same dataset down to 1.69 GiB, but increased server-side processing time slightly.

#### Compression reduces resource usage[​](#compression-reduces-resource-usage "Direct link to Compression reduces resource usage")

Compression not only reduces network traffic—it also improves CPU and memory efficiency on the server. With compressed data, ClickHouse receives fewer bytes and spends less time parsing large inputs. This benefit is especially important when ingesting from multiple concurrent clients, such as in observability scenarios.

The impact of compression on CPU and memory is modest for LZ4, and moderate for ZSTD. Even under load, server-side efficiency improves due to the reduced data volume.

**Combining compression with batching and an efficient input format (like Native) yields the best ingestion performance.**

When using the native interface (e.g. [clickhouse-client](/docs/interfaces/cli)), LZ4 compression is enabled by default. You can optionally switch to ZSTD via settings.

With the [HTTP interface](/docs/interfaces/http), use the Content-Encoding header to apply compression (e.g. Content-Encoding: lz4). The entire payload must be compressed before sending.

### Pre-sort if low cost[​](#pre-sort-if-low-cost "Direct link to Pre-sort if low cost")

Pre-sorting data by primary key before insertion can improve ingestion efficiency in ClickHouse, particularly for large batches.

When data arrives pre-sorted, ClickHouse can skip or simplify the internal sorting step during part creation, reducing CPU usage and accelerating the insert process. Pre-sorting also improves compression efficiency, since similar values are grouped together—enabling codecs like LZ4 or ZSTD to achieve a better compression ratio. This is especially beneficial when combined with large batch inserts and compression, as it reduces both the processing overhead and the amount of data transferred.

**That said, pre-sorting is an optional optimization—not a requirement.** ClickHouse sorts data highly efficiently using parallel processing, and in many cases, server-side sorting is faster or more convenient than pre-sorting client-side.

**We recommend pre-sorting only if the data is already nearly ordered or if client-side resources (CPU, memory) are sufficient and underutilized.** In latency-sensitive or high-throughput use cases, such as observability, where data arrives out of order or from many agents, it's often better to skip pre-sorting and rely on ClickHouse's built-in performance.

## Asynchronous inserts[​](#asynchronous-inserts "Direct link to Asynchronous inserts")

Asynchronous inserts in ClickHouse provide a powerful alternative when client-side batching isn't feasible. This is especially valuable in observability workloads, where hundreds or thousands of agents send data continuously—logs, metrics, traces—often in small, real-time payloads. Buffering data client-side in these environments increases complexity, requiring a centralized queue to ensure sufficiently large batches can be sent.

Note

Sending many small batches in synchronous mode isn't recommended, leading to many parts being created. This will lead to poor query performance and ["too many part"](/docs/knowledgebase/exception-too-many-parts) errors.

Asynchronous inserts shift batching responsibility from the client to the server by writing incoming data to an in-memory buffer, then flushing it to storage based on configurable thresholds. This approach significantly reduces part creation overhead, lowers CPU usage, and ensures ingestion remains efficient—even under high concurrency.

The core behavior is controlled via the [`async_insert`](/docs/operations/settings/settings#async_insert) setting.

Asynchronous inserts are supported over both the HTTP and native TCP interfaces.

When enabled (`async_insert = 1`), inserts are buffered and only written to disk once one of the flush conditions is met:

- The buffer reaches a specified data size ([`async_insert_max_data_size`](/docs/operations/settings/settings#async_insert_max_data_size), default 10 MiB).
- A time threshold elapses ([`async_insert_busy_timeout_ms`](/docs/operations/settings/settings#async_insert_busy_timeout_max_ms), default 200 ms or 1000 ms on Cloud).
- A maximum number of insert queries accumulate ([`async_insert_max_query_number`](/docs/operations/settings/settings#async_insert_max_query_number), default 450).

Whichever threshold is reached first triggers the flush.

This batching process is invisible to clients and helps ClickHouse efficiently merge insert traffic from multiple sources. However, until a flush occurs, the data can't be queried. Importantly, there are multiple buffers per insert shape and settings combination, and in clusters, buffers are maintained per node—enabling fine-grained control across multi-tenant environments. Insert mechanics are otherwise identical to those described for [synchronous inserts](/docs/best-practices/selecting-an-insert-strategy#synchronous-inserts-by-default).

### Choosing a return mode[​](#choosing-a-return-mode "Direct link to Choosing a return mode")

The behavior of asynchronous inserts is further refined using the [`wait_for_async_insert`](/docs/operations/settings/settings#wait_for_async_insert) setting.

When set to 1 (the default), ClickHouse only acknowledges the insert after the data is successfully flushed to disk. This ensures strong durability guarantees and makes error handling straightforward: if something goes wrong during the flush, the error is returned to the client. This mode is recommended for most production scenarios, especially when insert failures must be tracked reliably.

[Benchmarks](https://clickhouse.com/blog/asynchronous-data-inserts-in-clickhouse) show it scales well with concurrency—whether you're running 200 or 500 clients—thanks to adaptive inserts and stable part creation behavior.