Learning Platform
Глоссарий Troubleshooting
Урок 12.01 · 30 мин
Продвинутый
async insertdeduplicationbatchingingestion

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

Классическая проблема при высокочастотной загрузке данных в ClickHouse — “too many parts”. Если клиент отправляет тысячи мелких INSERT в секунду, каждый INSERT создаёт отдельный part на диске. Фоновый merge не успевает объединять их — число parts растёт, производительность падает, и ClickHouse начинает отклонять вставки с ошибкой Too many parts.

Решение — async_insert=1: ClickHouse накапливает данные из нескольких клиентских INSERT в буфере и делает одну физическую запись на диск единым батчем.


Как работает async_insert

При async_insert=1 клиентский INSERT не записывает данные немедленно. Вместо этого:

  1. Данные попадают в in-memory буфер на стороне сервера
  2. Буфер накапливается до достижения порогового размера (async_insert_max_data_size) или таймаута (async_insert_busy_timeout_max_ms)
  3. Когда порог достигнут — весь буфер записывается как единый part
Путь данных при async_insert=1
Client INSERT (async_insert=1)Client INSERT: клиент отправляет данные с настройкой async_insert=1. Запрос возвращается клиенту сразу (или после подтверждения при wait_for_async_insert=1), не дожидаясь физической записи на диск.
данные в буфер
Async buffer (in-memory)Async buffer: in-memory буфер на стороне сервера. Данные от нескольких клиентских INSERT накапливаются здесь. Буфер разделён по таблицам. Максимальный размер буфера: async_insert_max_data_size (по умолчанию 10 МБ).
при достижении порога или таймаута
Batch INSERT → MergeTree partBatch INSERT to MergeTree: когда буфер достигает async_insert_max_data_size или истекает async_insert_busy_timeout_max_ms — весь буфер записывается как единый part. Один part вместо тысяч мелких частей.

Ключевые настройки

Включение и подтверждение

-- Включить async insert для сессии
SET async_insert = 1;

-- Ждать подтверждения от сервера (durability mode)
SET wait_for_async_insert = 1;

-- Не ждать подтверждения (fire-and-forget, максимальный throughput)
SET wait_for_async_insert = 0;

Настройки можно задавать на уровне пользователя в конфиге сервера или передавать в каждом запросе.

Параметры буфера и таймаутов

<!-- /etc/clickhouse-server/users.d/async_insert.xml -->
<clickhouse>
  <profiles>
    <default>
      <!-- Включить async insert по умолчанию (26.3 LTS: включён глобально) -->
      <async_insert>1</async_insert>
      <!-- Ждать подтверждения (рекомендуется для durability) -->
      <wait_for_async_insert>1</wait_for_async_insert>
      <!-- Максимальный размер буфера: 10 МБ -->
      <async_insert_max_data_size>10485760</async_insert_max_data_size>
      <!-- Адаптивный таймаут сброса буфера: до 1 с (26.3 LTS) -->
      <async_insert_busy_timeout_max_ms>1000</async_insert_busy_timeout_max_ms>
    </default>
  </profiles>
</clickhouse>

Адаптивный таймаут (async_insert_busy_timeout_max_ms, введён в 26.3 LTS) — ClickHouse адаптивно увеличивает задержку сброса буфера при низкой нагрузке. При высокой нагрузке буфер сбрасывается быстро; при низкой — небольшая задержка позволяет собрать больше данных в один batch.


Дедупликация при retry

При network failure клиент не знает, был ли INSERT успешным. Повторная отправка может создать дубли. Для идемпотентных вставок используется insert_deduplication_token:

-- Каждая вставка получает уникальный токен
INSERT INTO events SETTINGS
  async_insert = 1,
  wait_for_async_insert = 1,
  insert_deduplication_token = 'batch-2026-01-15-001'
VALUES (...);

-- При retry с тем же токеном — вставка пропускается (идемпотентность)
INSERT INTO events SETTINGS
  async_insert = 1,
  wait_for_async_insert = 1,
  insert_deduplication_token = 'batch-2026-01-15-001'
VALUES (...);  -- дубль пропущен

Токен хранится в Keeper в узле blocks/ таблицы. Если блок с таким токеном уже есть — ClickHouse возвращает успех без повторной записи.


ClickHouse 26.3 LTS: async_insert по умолчанию

Начиная с ClickHouse 26.3 LTS, async_insert = 1 — значение по умолчанию для пользовательского профиля default. Это значит: HTTP-интерфейс, native protocol и любые клиенты, которые явно не выставляли async_insert = 0, автоматически получают server-side batching без изменения клиентского кода.

Что значит “по умолчанию” в 26.3

В предыдущих LTS (24.3, 24.8) async_insert был opt-in — нужно было ставить SET async_insert = 1 или прописывать в profile XML. В 26.3 переключение симметрично: чтобы вернуть классическое поведение per-INSERT-part, теперь нужно явно SET async_insert = 0. Дефолт сместился.

Этот сдвиг даёт три следствия для миграции с 24.x → 26.3:

  1. High-frequency clients перестают получать “Too many parts” — без изменений в коде. Это первая и главная причина переключения дефолта.
  2. wait_for_async_insert = 1 остаётся дефолтом — сервер ждёт физического flush перед 200 OK. Durability-семантика классического sync-INSERT сохранена.
  3. Latency распределения per-INSERT увеличивается на величину busy-timeout (по умолчанию 200ms-1s в зависимости от нагрузки) — адаптивный таймаут стартует с малого значения и растёт при низкой частоте вставок.

Batching policy и flush triggers

В 26.3 буфер сбрасывается при выполнении любого из трёх условий (whichever comes first):

ТриггерПараметрДефолт 26.3
Размер накопленных данныхasync_insert_max_data_size10 MiB
Количество накопленных запросовasync_insert_max_query_number450
Таймаут с момента первой записи в буферasync_insert_busy_timeout_max_ms1000 ms (адаптивно)

Адаптивный таймаут (async_insert_busy_timeout_max_ms) — ключевая фича 26.3: ClickHouse начинает с малой задержки (~50ms) и адаптивно увеличивает её, если входящих запросов мало. При взрывной нагрузке таймаут падает до минимума, и flush происходит почти моментально по data-size триггеру.

Server-side dedup для async insert

Insert deduplication для async-вставок работает на уровне финального merged-блока (не отдельных клиентских INSERT). После сборки batch ClickHouse считает hash объединённых данных и сверяет его с deduplication-окном в Keeper. Это означает: два одинаковых клиентских INSERT, попавшие в один batch, будут дедуплицированы как часть batch hash, но если они попали в разные batch — они станут разными blocks, и для гарантии нужна явная insert_deduplication_token (см. секцию выше).

-- Включить дедупликацию для async вставок (включена по умолчанию в 26.3)
SET async_insert_deduplicate = 1;

-- Размер deduplication-окна на уровне Keeper (число последних блоков)
-- Применяется к target ReplicatedMergeTree, не к async-buffer:
ALTER TABLE events MODIFY SETTING
    replicated_deduplication_window = 1000,
    replicated_deduplication_window_seconds = 604800;
WARNING

wait_for_async_insert=0 означает, что сервер подтверждает получение данных до их физической записи на диск. При сбое сервера после подтверждения, но до сброса буфера — данные будут потеряны. Используйте wait_for_async_insert=1 (это дефолт в 26.3) для сценариев, где потеря данных недопустима (финансовые транзакции, события безопасности).


Мониторинг async insert

-- Посмотреть текущее состояние async insert буферов
SELECT *
FROM system.asynchronous_insert_log
ORDER BY event_time DESC
LIMIT 10;

-- Количество успешных и неудачных async inserts
SELECT
    status,
    count() AS cnt,
    sum(rows) AS total_rows
FROM system.asynchronous_insert_log
WHERE event_time > now() - INTERVAL 1 HOUR
GROUP BY status;

Ключевые выводы

  1. async_insert=1 решает проблему “too many parts” при высокочастотных вставках: данные буферизуются на сервере и записываются единым batch.
  2. wait_for_async_insert=1 — durability mode: клиент ждёт подтверждения записи. =0 — fire-and-forget с риском потери данных при сбое.
  3. insert_deduplication_token обеспечивает идемпотентность при retry-логике клиента.
  4. async_insert_busy_timeout_max_ms (26.3 LTS) — адаптивный таймаут: при высокой нагрузке буфер сбрасывается быстро, при низкой — небольшая задержка улучшает batching.
  5. В ClickHouse 26.3 LTS async_insert = 1 — значение по умолчанию для профиля default. Чтобы вернуть классическое поведение, нужно явно SET async_insert = 0. Flush-триггеры: 10 MiB / 450 запросов / 1000ms адаптивного таймаута — whichever comes first. Insert deduplication работает на уровне merged-блока batch (не клиентских INSERT) — для строгой идемпотентности на уровне отдельной вставки используйте insert_deduplication_token.
Streaming-вставки: микробатчи, буферизация и exactly-once гарантии WAL в PostgreSQL: запись на диск, fsync и durability гарантии

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. Разработчик настроил async_insert=1 и wait_for_async_insert=0 для клиента IoT-платформы. Во время планового обновления сервера ClickHouse буфер async insert содержал 50 000 событий, которые ещё не были записаны на диск. Что произойдёт с этими данными?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 11