Kafka хранит сообщения в журнале. Consumer читает их и передаёт в ClickHouse, где они становятся строками аналитических таблиц.
Топик, партиция и offset
Топик объединяет поток сообщений. Он разделён на партиции; внутри каждой сообщения имеют последовательные offsets — позиции в журнале. Глобального порядка между всеми партициями нет.
Consumer group объединяет читателей, которые распределяют между собой партиции. В один момент партицию группы читает один активный consumer. Независимые группы могут читать тот же поток для разных задач. Число партиций ограничивает полезный параллелизм чтения одной группы.
Подтверждение offset означает, что группа считает соответствующую позицию обработанной. Начальная позиция новой группы и срок хранения Kafka определяют, какую историю можно прочитать. Сообщение, уже удалённое по retention, нельзя восстановить одним уменьшением offset.
Ключ сообщения может определять партицию через хеш: одинаковые ключи при неизменном числе партиций и том же partitioner попадают в одну партицию. Это помогает сохранять порядок изменений одной сущности, но популярный ключ способен перегрузить её. Producer может также явно указать партицию или использовать другую стратегию.
Без ключа выбор зависит от клиента, версии и настроек. Round-robin последовательно обходит партиции; случайный выбор даёт приблизительное распределение большого потока. Стандартный Java producer использует sticky-подход: некоторое время отправляет в одну партицию, собирая batch; адаптивная настройка может учитывать скорость брокеров. Нагрузку оценивают также по байтам и стоимости обработки сообщений.
Kafka engine и целевая таблица
Распространённый путь состоит из четырёх элементов:
Kafka topic → Kafka engine → materialized view → MergeTreeKafka engine задаёт подключение, топик, группу и формат сообщений. Он служит источником чтения, а не долговременным аналитическим хранилищем. Materialized view разбирает и преобразует поступающие блоки, затем записывает их в обычную таблицу MergeTree. SELECT напрямую из Kafka-таблицы не следует трактовать как безвредный просмотр сохранённой копии.
Kafka Connect и materialized view
Kafka Connect — среда выполнения коннекторов между Kafka и внешними системами. Source connector отправляет данные источника в Kafka, sink connector читает топики и записывает данные во внешнюю систему. ClickHouse sink работает вне сервера ClickHouse и пишет непосредственно в целевую таблицу:
Kafka topic → Kafka Connect sink → MergeTreeПосле Connect-приёмника можно добавить инкрементальную materialized view для отдельной витрины. Сам Connect sink пишет прямо в целевую таблицу; Kafka engine в этом маршруте не участвует.
У маршрутов engine, Connect и управляемых интеграций ClickHouse Cloud отличаются расположение consumer и гарантии доставки. У ClickHouse Connect Sink есть режим exactly-once, но он выключен по умолчанию и требует настройки. Его гарантии зависят от версии, конфигурации и границы результата. Сам режим exactly-once не обеспечивает уникальность бизнес-ключа или автоматическую корректность любой витрины.
CDC и потоковая загрузка
CDC, Change Data Capture, передаёт изменения источника: insert, update и delete. Например, Debezium читает изменения PostgreSQL из WAL через механизм логической репликации и отправляет события в Kafka. Kafka хранит полученные сообщения; чтение журнала БД выполняет коннектор.
При первом подключении обычно нужен snapshot — согласованный снимок существующих строк. Затем коннектор продолжает чтение изменений от согласованной позиции журнала, чтобы не потерять изменения во время снимка. Snapshot показывает состояние на момент чтения, а не все прежние версии. Удалённая до подключения история не появится автоматически: для неё нужен сохранённый журнал, архив или другой источник истории.
Потоковая загрузка непрерывно принимает новые данные, часто небольшими batches. Задержка складывается из захвата изменений, ожидания batch, доставки и обработки в ClickHouse. Событие update или delete не превращается само в изменение строки MergeTree: формат события и модель приёмника должны учитывать стабильный ключ, версию и признак удаления. Для последнего состояния подходит версионная модель ReplacingMergeTree; для истории сохраняют события. Порядок поступления и последующая дедупликация не гарантируют корректный повторный вклад в агрегат.
Повторная доставка
Для обычного Kafka-engine пути разумно исходить из at-least-once: сообщение может попасть в обработку больше одного раза. Если данные уже записаны, а подтверждение offset потерялось, повторное чтение добавит тот же вклад.
Стабильный event_id помогает распознать повтор, но MergeTree не проверяет его уникальность. ReplacingMergeTree может схлопнуть состояние позже; инкрементальная витрина к этому времени уже способна учесть обе вставки. Поэтому место дедупликации выбирают до необратимой агрегации либо применяют модель корректировок и пересчёта.
Идемпотентность означает, что повтор одной операции сохраняет результат первого успешного применения. Для неё определяют идентичность события, окно хранения идентификаторов и обработку поздних событий.
Некорректные сообщения и задержка
Ошибка формата или несовместимое поле может остановить обработку блока. Пропуск некорректных сообщений уменьшает полноту данных, а бесконечный повтор мешает последующим корректным сообщениям. Политика должна определять, где сохраняется ошибочное событие и как его исправить или явно исключить.
Lag — разница между доступной позицией журнала и подтверждённой позицией группы. Он помогает увидеть отставание чтения, но не доказывает полноту: группа могла начать с конца, пропустить ошибки или загрузить неверные значения. Важны также задержка от времени события до таблицы и сверка источника с результатом.
При проверке потока полезен короткий сценарий: корректное сообщение, ошибочное, снова корректное. Он показывает, продолжает ли consumer работать после ошибки. Рабочий HTTP-процесс ClickHouse сам по себе этого не подтверждает.