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

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

Эффективный приём данных лежит в основе высокопроизводительных развертываний 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, ⑩ сортирует строки по столбцам первичного ключа, если они ещё не были предварительно отсортированы, ⑪ создаёт разреженный первичный индекс, ⑫ применяет покомпонентное сжатие и ⑬ записывает данные как новую ⑭ часть данных на диск.

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

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

Мы рекомендуем вставлять данные пакетами как минимум по 1 000 строк, а в идеале — от 10 000 до 100 000 строк. Более редкие, но более крупные вставки снижают количество записываемых кусков (parts), минимизируют нагрузку на слияния и уменьшают общее потребление ресурсов системы.

Для того чтобы стратегия синхронных вставок была эффективной, требуется такое клиентское пакетирование.

Если вы не можете выполнять пакетирование данных на стороне клиента, ClickHouse поддерживает асинхронные вставки, которые переносят пакетирование на сервер (см.).

Совет

Независимо от размера ваших вставок мы рекомендуем поддерживать количество запросов на вставку на уровне примерно одного в секунду. Причина этой рекомендации в том, что созданные куски (parts) в фоновом режиме объединяются в более крупные (для оптимизации данных под запросы на чтение), и отправка слишком большого числа запросов на вставку в секунду может привести к ситуации, когда фоновое слияние не будет успевать за количеством новых кусков. Однако вы можете использовать более высокую частоту запросов на вставку при использовании асинхронных вставок (см. асинхронные вставки).

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

Синхронные вставки также идемпотентны. При использовании движков 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.

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

Асинхронные вставки в ClickHouse предоставляют мощную альтернативу в случаях, когда пакетирование на стороне клиента невозможно. Это особенно ценно в нагрузках по наблюдаемости (observability), где сотни или тысячи агентов непрерывно отправляют данные — логи, метрики, трейсы — часто небольшими порциями в режиме реального времени. Буферизация данных на стороне клиента в таких средах повышает сложность и требует централизованной очереди, чтобы обеспечивать отправку достаточно крупных пакетов.

Примечание

Отправка множества маленьких батчей в синхронном режиме не рекомендуется, поскольку приводит к созданию большого количества кусков (parts). Это ухудшит производительность запросов и приведёт к ошибкам "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 эффективно объединять трафик вставок из нескольких источников. Однако до момента сброса данные недоступны для запросов. Важно, что существует несколько буферов для каждой комбинации типа вставки (insert shape) и настроек, а в кластерах буферы поддерживаются на каждом узле — это обеспечивает тонкий контроль в многопользовательских средах. Механика вставок в остальном идентична описанной для синхронных вставок.

Выбор режима возврата

Поведение асинхронных вставок дополнительно уточняется с помощью настройки wait_for_async_insert.

При значении 1 (по умолчанию) ClickHouse подтверждает вставку только после того, как данные успешно сброшены на диск. Это обеспечивает сильные гарантии надёжного сохранения данных и упрощает обработку ошибок: если что-то идёт не так во время сброса, ошибка возвращается клиенту. Этот режим рекомендуется для большинства продукционных сценариев, особенно когда отказы вставок должны надёжно отслеживаться.

Результаты бенчмарков показывают, что он хорошо масштабируется при высокой конкурентности — независимо от того, запускаете ли вы 200 или 500 клиентов — благодаря адаптивным вставкам и стабильному поведению по созданию кусков.

Установка wait_for_async_insert = 0 включает режим "fire-and-forget". В этом случае сервер подтверждает вставку сразу после буферизации данных, не дожидаясь их записи в хранилище.

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

Бенчмарки также демонстрируют значительное сокращение числа кусков и снижение использования CPU при редких сбросах буфера (например, каждые 30 секунд), но риск «тихих» сбоев при этом сохраняется.

Мы настоятельно рекомендуем использовать async_insert=1,wait_for_async_insert=1, если вы применяете асинхронные вставки. Использование wait_for_async_insert=0 очень рискованно, поскольку ваш клиент INSERT может не узнать о возникших ошибках, а также может привести к потенциальной перегрузке, если клиент продолжит быстро записывать данные в ситуации, когда сервер ClickHouse должен замедлить запись и создать некоторое обратное давление (backpressure), чтобы обеспечить надёжность сервиса.

Дедупликация и надёжность

По умолчанию ClickHouse выполняет автоматическую дедупликацию для синхронных вставок, что делает повторные попытки безопасными в случае сбоев. Однако для асинхронных вставок она отключена, если только не включена явно (её не следует включать, если у вас есть зависимые материализованные представления — см. issue).

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

Включение асинхронных вставок

Асинхронные вставки можно включить для конкретного пользователя или для определённого запроса:

  • Включение асинхронных вставок на уровне пользователя. В этом примере используется пользователь default; если вы создадите другого пользователя, подставьте его имя:

    ALTER USER default SETTINGS async_insert = 1
    
  • Вы можете задать параметры асинхронной вставки, используя предложение SETTINGS в запросах INSERT:

    INSERT INTO YourTable SETTINGS async_insert=1, wait_for_async_insert=1 VALUES (...)
    
  • Вы также можете указать параметры асинхронной вставки как параметры подключения при использовании клиентской библиотеки ClickHouse для выбранного языка программирования.

    В качестве примера ниже показано, как это можно сделать в строке подключения JDBC при использовании ClickHouse Java JDBC-драйвера для подключения к 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), сжатие применяется ко всей полезной нагрузке целиком, а не к отдельным блокам данных. Этот интерфейс часто предпочитают в средах, где простота протокола, балансировка нагрузки или широкая совместимость форматов важнее максимальной производительности.

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