Перейти к основному содержанию
Перейти к основному содержанию

Выбор стратегии вставки

Эффективный приём данных лежит в основе высокопроизводительных развертываний ClickHouse. Выбор правильной стратегии вставки может существенно повлиять на пропускную способность, стоимость и надежность. В этом разделе изложены передовые практики, компромиссы и варианты конфигурации, которые помогут вам принять правильное решение для вашей нагрузки.

Примечание

В дальнейшем предполагается, что вы отправляете данные в ClickHouse через клиент. Если вы загружаете данные в ClickHouse, например, используя встроенные табличные функции, такие как s3 и gcs, мы рекомендуем наше руководство "Optimizing for S3 Insert and Read Performance".

Синхронные вставки по умолчанию

По умолчанию вставки в ClickHouse являются синхронными. Каждый запрос вставки немедленно создаёт часть данных в хранилище на диске, включая метаданные и индексы.

Используйте синхронные вставки, если можете выполнять батчинг данных на стороне клиента

Если нет, см. раздел Асинхронные вставки ниже.

Ниже мы кратко рассмотрим механизм вставки в таблицы MergeTree в ClickHouse:

Insert processes

Шаги на стороне клиента

Для оптимальной производительности данные должны быть ① собраны в батчи, поэтому размер батча — это первое решение.

ClickHouse сохраняет вставленные данные на диск, упорядоченными по столбцу(ам) первичного ключа таблицы. Второе решение — нужно ли ② предварительно отсортировать данные перед передачей на сервер. Если батч приходит предварительно отсортированным по столбцу(ам) первичного ключа, ClickHouse может пропустить шаг ⑩ сортировки, ускоряя ингестию.

Если данные для приёма не имеют заранее определённого формата, ключевое решение — выбор формата. ClickHouse поддерживает вставку данных в более чем 70 форматах. Однако при использовании командного клиента ClickHouse или клиентов для языков программирования этот выбор часто выполняется автоматически. При необходимости этот автоматический выбор также можно явно переопределить.

Следующее важное решение — ④ сжимать ли данные перед передачей на сервер ClickHouse. Сжатие уменьшает объём передаваемых данных и повышает эффективность сети, что приводит к более быстрой передаче данных и меньшему использованию полосы пропускания, особенно для больших наборов данных.

Данные ⑤ передаются в сетевой интерфейс ClickHouse — либо native, либо HTTP интерфейс (которые мы сравним позже в этом материале).

Шаги на стороне сервера

После ⑥ получения данных ClickHouse ⑦ распаковывает их, если использовалось сжатие, затем ⑧ разбирает их из исходного формата.

Используя значения из этих отформатированных данных и DDL целевой таблицы, ClickHouse ⑨ строит в памяти блок в формате MergeTree, ⑩ сортирует строки по столбцам первичного ключа, если они ещё не были предварительно отсортированы, ⑪ создаёт разреженный первичный индекс, ⑫ применяет покомпонентное сжатие и ⑬ записывает данные как новую ⑭ часть данных на диск.

Используйте батчевые вставки при синхронном режиме

The above mechanics illustrate a constant overhead regardless of the insert size, making batch size the single most important optimization for ingest throughput. Batching inserts reduce the overhead as a proportion of total insert time and improves processing efficiency.

We recommend inserting data in batches of at least 1,000 rows, and ideally between 10,000–100,000 rows. Fewer, larger inserts reduce the number of parts written, minimize merge load, and lower overall system resource usage.

For a synchronous insert strategy to be effective this client-side batching is required.

If you're unable to batch data client-side, ClickHouse supports asynchronous inserts that shift batching to the server (see).

Совет

Regardless of the size of your inserts, we recommend keeping the number of insert queries around one insert query per second. The reason for this recommendation is that the created parts are merged to larger parts in the background (in order to optimize your data for read queries), and sending too many insert queries per second can lead to situations where the background merging can't keep up with the number of new parts. However, you can use a higher rate of insert queries per second when you use asynchronous inserts (see asynchronous inserts).

Обеспечьте идемпотентность повторных попыток

Синхронные вставки также идемпотентны. При использовании движков MergeTree ClickHouse по умолчанию дедуплицирует вставки. Это защищает от неоднозначных случаев сбоев, например:

  • Вставка завершилась успешно, но клиент не получил подтверждение из-за сетевого сбоя.
  • Вставка завершилась с ошибкой на стороне сервера и привела к тайм-ауту.

В обоих случаях безопасно повторить вставку, если состав батча и порядок данных остаются идентичными. По этой причине критически важно, чтобы клиенты выполняли повторные попытки последовательно, не изменяя и не переупорядочивая данные.

Выберите правильную таблицу для вставки

Для шардированных кластеров есть два варианта:

  • Вставлять данные напрямую в таблицу MergeTree или ReplicatedMergeTree. Это наиболее эффективный вариант, когда клиент может самостоятельно распределять нагрузку между шардами. При internal_replication = true ClickHouse прозрачно обрабатывает репликацию.
  • Вставлять данные в таблицу Distributed. Это позволяет клиентам отправлять данные на любой узел и поручить ClickHouse пересылку на нужный шард. Это проще, но немного менее эффективно из-за дополнительного шага пересылки. internal_replication = true по-прежнему рекомендуется.

В ClickHouse Cloud все узлы читают и записывают в один и тот же шард (single shard). Вставки автоматически балансируются между узлами. Пользователи могут просто отправлять вставки на публичную конечную точку (endpoint).

Выберите правильный формат

Выбор правильного входного формата критически важен для эффективной ингестии данных в ClickHouse. При наличии более чем 70 поддерживаемых форматов выбор наиболее производительного варианта может существенно повлиять на скорость вставки, использование CPU и памяти, а также общую эффективность системы.

Хотя гибкость важна для data engineering-задач и файловых импортов, приложениям следует отдавать приоритет форматам, ориентированным на производительность:

  • Native format (рекомендуется): Наиболее эффективный. Колонко-ориентированный формат, на стороне сервера требуется минимальный парсинг. По умолчанию используется в Go- и Python-клиентах.
  • RowBinary: Эффективный строковый формат, подходит, если сложно выполнить колонночное преобразование на стороне клиента. Используется Java-клиентом.
  • JSONEachRow: Прост в использовании, но дорог в парсинге. Подходит для сценариев с низким объёмом данных или для быстрого создания интеграций.

Используйте сжатие

Сжатие играет ключевую роль в снижении сетевых накладных расходов, ускорении вставок и уменьшении затрат на хранение в ClickHouse. При грамотном использовании оно повышает производительность ингестии без необходимости изменять формат данных или схему.

Сжатие данных для вставки уменьшает размер полезной нагрузки, отправляемой по сети, минимизируя использование пропускной способности и ускоряя передачу.

Для вставок сжатие особенно эффективно в сочетании с форматом Native, который уже соответствует внутренней колонночной модели хранения ClickHouse. В такой конфигурации сервер может эффективно распаковать и напрямую сохранить данные с минимальными преобразованиями.

Используйте LZ4 для скорости, ZSTD для степени сжатия

ClickHouse поддерживает несколько кодеков сжатия при передаче данных. Два распространённых варианта:

  • LZ4: Быстрый и лёгкий. Существенно уменьшает размер данных с минимальной нагрузкой на CPU, что делает его идеальным для вставок с высокой пропускной способностью и вариантом по умолчанию в большинстве клиентов ClickHouse.
  • ZSTD: Обеспечивает более высокую степень сжатия, но более ресурсоёмок по CPU. Полезен, когда высока стоимость сетевой передачи — например, при межрегиональной передаче данных или между облачными провайдерами, — хотя это немного увеличивает вычислительную нагрузку на клиенте и время распаковки на сервере.

Рекомендуемая практика: используйте LZ4, если только у вас нет ограниченной пропускной способности или значимых затрат на исходящий трафик — в этом случае рассмотрите использование ZSTD.

Примечание

В тестах бенчмарка FastFormats вставки в формате Native, сжатые LZ4, уменьшили размер данных более чем на 50%, сократив время ингестии с 150 до 131 секунды для набора данных объёмом 5,6 GiB. Переход на ZSTD сжал тот же набор данных до 1,69 GiB, но немного увеличил время обработки на стороне сервера.

Сжатие снижает использование ресурсов

Сжатие не только уменьшает сетевой трафик — оно также повышает эффективность использования CPU и памяти на сервере. При сжатых данных ClickHouse принимает меньше байт и тратит меньше времени на парсинг больших объёмов входных данных. Этот эффект особенно важен при приёме данных от нескольких параллельных клиентов, например, в сценариях наблюдаемости (observability).

Влияние сжатия на CPU и память невелико для LZ4 и умеренно для ZSTD. Даже под нагрузкой эффективность сервера повышается за счёт уменьшения объёма данных.

Сочетание сжатия с батчированием и эффективным входным форматом (например, Native) даёт наилучшую производительность ингестии.

При использовании нативного интерфейса (например, clickhouse-client) сжатие LZ4 включено по умолчанию. При необходимости вы можете переключиться на ZSTD через настройки.

С HTTP-интерфейсом используйте заголовок Content-Encoding для применения сжатия (например, Content-Encoding: lz4). Вся полезная нагрузка должна быть сжата перед отправкой.

Предварительно сортируйте данные, если это недорого

Предварительная сортировка данных по первичному ключу перед вставкой может повысить эффективность ингестии в ClickHouse, особенно для крупных пакетов вставки.

Когда данные приходят предварительно отсортированными, ClickHouse может пропустить или упростить внутренний шаг сортировки при создании парта, снижая использование CPU и ускоряя процесс вставки. Предварительная сортировка также улучшает эффективность сжатия, поскольку схожие значения группируются вместе — это позволяет таким кодекам, как LZ4 или ZSTD, достигать более высокой степени сжатия. Это особенно полезно в сочетании с крупными пакетными вставками и сжатием, так как уменьшает и вычислительные накладные расходы, и объём передаваемых данных.

Тем не менее предварительная сортировка — это опциональная оптимизация, а не требование. ClickHouse очень эффективно сортирует данные с использованием параллельной обработки, и во многих случаях сортировка на стороне сервера быстрее или удобнее, чем предварительная сортировка на стороне клиента.

Мы рекомендуем выполнять предварительную сортировку только в том случае, если данные уже почти отсортированы или если ресурсы на стороне клиента (CPU, память) достаточны и при этом простаивают. В сценариях, чувствительных к задержкам, или при высокой нагрузке, например в системах наблюдаемости, когда данные поступают вразнобой или от множества агентов, часто лучше отказаться от предварительной сортировки и полагаться на встроенную производительность ClickHouse.

Асинхронные вставки

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.

Примечание

Sending many small batches in synchronous mode is not recommended, leading to many parts being created. This will lead to poor query performance and "too many part" 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 setting.

Async inserts

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

(1) the buffer reaches a specified size (async_insert_max_data_size) (2) a time threshold elapses (async_insert_busy_timeout_ms) or (3) a maximum number of insert queries accumulate (async_insert_max_query_number).

This batching process is invisible to clients and helps ClickHouse efficiently merge insert traffic from multiple sources. However, until a flush occurs, the data cannot 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.

Choosing a return mode

The behavior of asynchronous inserts is further refined using the 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 show it scales well with concurrency—whether you're running 200 or 500 clients—thanks to adaptive inserts and stable part creation behavior.

Setting wait_for_async_insert = 0 enables "fire-and-forget" mode. Here, the server acknowledges the insert as soon as the data is buffered, without waiting for it to reach storage.

This offers ultra-low-latency inserts and maximal throughput, ideal for high-velocity, low-criticality data. However, this comes with trade-offs: there's no guarantee the data will be persisted, errors may only surface during flush, and it's difficult to trace failed inserts. Use this mode only if your workload can tolerate data loss.

Benchmarks also demonstrate substantial part reduction and lower CPU usage when buffer flushes are infrequent (e.g. every 30 seconds), but the risk of silent failure remains.

Our strong recommendation is to use async_insert=1,wait_for_async_insert=1 if using asynchronous inserts. Using wait_for_async_insert=0 is very risky because your INSERT client may not be aware if there are errors, and also can cause potential overload if your client continues to write quickly in a situation where the ClickHouse server needs to slow down the writes and create some backpressure in order to ensure reliability of the service.

Deduplication and reliability

By default, ClickHouse performs automatic deduplication for synchronous inserts, which makes retries safe in failure scenarios. However, this is disabled for asynchronous inserts unless explicitly enabled (this should not be enabled if you have dependent materialized views—see issue).

In practice, if deduplication is turned on and the same insert is retried—due to, for instance, a timeout or network drop—ClickHouse can safely ignore the duplicate. This helps maintain idempotency and avoids double-writing data. Still, it's worth noting that insert validation and schema parsing happen only during buffer flush—so errors (like type mismatches) will only surface at that point.

Enabling asynchronous inserts

Asynchronous inserts can be enabled for a particular user, or for a specific query:

  • Enabling asynchronous inserts at the user level. This example uses the user default, if you create a different user then substitute that username:

    ALTER USER default SETTINGS async_insert = 1
    
  • You can specify the asynchronous insert settings by using the SETTINGS clause of insert queries:

    INSERT INTO YourTable SETTINGS async_insert=1, wait_for_async_insert=1 VALUES (...)
    
  • You can also specify asynchronous insert settings as connection parameters when using a ClickHouse programming language client.

    As an example, this is how you can do that within a JDBC connection string when you use the ClickHouse Java JDBC driver for connecting to ClickHouse Cloud:

    "jdbc:ch://HOST.clickhouse.cloud:8443/?user=default&password=PASSWORD&ssl=true&custom_http_params=async_insert=1,wait_for_async_insert=1"
    

Выберите интерфейс — HTTP или native

Native

ClickHouse предлагает два основных интерфейса для ингестии данных: native-интерфейс и HTTP-интерфейс — каждый со своими компромиссами между производительностью и гибкостью. Native-интерфейс, используемый clickhouse-client и рядом клиентских библиотек для языков, таких как Go и C++, специально оптимизирован под производительность. Он всегда передаёт данные в высокоэффективном формате Native ClickHouse, поддерживает блочное сжатие с помощью LZ4 или ZSTD и минимизирует серверную обработку, перекладывая такие задачи, как парсинг и конвертация форматов, на клиента.

Он даже позволяет вычислять значения столбцов с MATERIALIZED и DEFAULT на стороне клиента, что даёт возможность серверу полностью пропустить эти шаги. Это делает native-интерфейс идеальным для сценариев высокопроизводительной ингестии данных, где критична эффективность.

HTTP

В отличие от многих традиционных баз данных, ClickHouse также поддерживает HTTP-интерфейс. Этот интерфейс, напротив, отдаёт приоритет совместимости и гибкости. Он позволяет отправлять данные в любом поддерживаемом формате — включая JSON, CSV, Parquet и другие — и широко поддерживается большинством клиентов ClickHouse, включая Python, Java, JavaScript и Rust.

Часто он предпочтительнее нативного протокола ClickHouse, поскольку позволяет легко маршрутизировать трафик с помощью балансировщиков нагрузки. Следует ожидать небольших различий в производительности вставки по сравнению с нативным протоколом, который имеет немного меньшие накладные расходы.

Однако он не обладает такой глубокой интеграцией, как нативный протокол, и не может выполнять оптимизации на стороне клиента, такие как вычисление материализованных значений или автоматическая конвертация в формат Native. Хотя вставки по HTTP по-прежнему могут сжиматься с использованием стандартных HTTP-заголовков (например, Content-Encoding: lz4), сжатие применяется ко всей полезной нагрузке целиком, а не к отдельным блокам данных. Этот интерфейс часто предпочитают в средах, где простота протокола, балансировка нагрузки или широкая совместимость форматов важнее максимальной производительности.

Более подробное описание этих интерфейсов приведено здесь.