ClickHouse оптимизирован для аналитических рабочих нагрузок (OLAP) и не предназначен для частых обновлений или UPSERT операций, как традиционные базы данных OLTP (например, MySQL, PostgreSQL). Однако он предоставляет обходные пути для обработки обновлений и UPSERT операций.


1. Почему ClickHouse не оптимизирован для UPDATE / UPSERT

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


2. Как выполнить UPDATE и UPSERT в ClickHouse

Хотя ClickHouse не поддерживает традиционные UPDATE / UPSERT операции, существуют различные способы достижения аналогичных результатов.

1. Использование ReplacingMergeTree (Лучший для дедупликации) ReplacingMergeTree сохраняет только последнюю версию строки (на основе столбца версии). Это работает хорошо, когда вы заменяете старые строки на новые.

Пример: Создание таблицы ReplacingMergeTree

CREATE TABLE users (
    id UInt32,
    name String,
    age UInt8,
    version UInt32
) ENGINE = ReplacingMergeTree(version)
ORDER BY id;
 

Эмуляция UPSERT (Insert + Update)

INSERT INTO users VALUES (1, 'Alice', 30, 1); -- Первоначальная вставка
INSERT INTO users VALUES (1, 'Alice', 31, 2); -- "Обновление" (новая версия)
 

Таблица хранит обе записи, но ClickHouse оставляет только последнюю версию во время фонового слияния.

2. Использование CollapsingMergeTree (Лучший для логического удаления) Этот движок полезен, когда вам нужны обновления и удаления на основе логов. Он отслеживает состояния BEGIN и END, позволяя логически удалять строки.

Пример: Использование CollapsingMergeTree

CREATE TABLE user_logs (
    id UInt32,
    name String,
    age UInt8,
    sign Int8  -- 1 для INSERT, -1 для DELETE
) ENGINE = CollapsingMergeTree(sign)
ORDER BY id;
 

Эмуляция логического DELETE

INSERT INTO user_logs VALUES (1, 'Alice', 30, 1);  -- Вставка
INSERT INTO user_logs VALUES (1, 'Alice', 30, -1); -- Отметка как удаленная
 

При слиянии ClickHouse удаляет логически удаленные строки.

3. Использование ReplacingMergeTree с FINAL (Для немедленного эффекта) Обычно ClickHouse выполняет слияние частей асинхронно, что означает, что обновления не видны немедленно. Используя FINAL, вы заставляете дедупликацию происходить немедленно.

Пример: Запрос с FINAL

SELECT * FROM users FINAL WHERE id = 1;

Это заставляет ClickHouse сохранять только последнюю версию.

4. Использование ALTER TABLE DELETE (Лучший для редких удалений) ClickHouse не поддерживает удаление строк на уровне записи, но предоставляет команду ALTER DELETE.

Пример: Удаление данных

SELECT * FROM users FINAL WHERE id = 1;

✅ Данные помечаются для удаления и физически удаляются в фоновом режиме.
❌ Удаления не мгновенные (происходят во время фонового слияния).
❌ Частые удаления могут быть неэффективными, так как ClickHouse работает по принципу только добавления данных.

5. Использование ALTER TABLE UPDATE (Лучший для редких обновлений) ClickHouse поддерживает UPDATE, но только в таблицах MergeTree, и это работает аналогично DELETE (путем пометки строк и переписывания частей).

Пример: Обновление данных

ALTER TABLE users UPDATE age = 32 WHERE id = 1;

✅ Работает, но ClickHouse переписывает целые части (а не отдельные строки).
❌ Дорого для больших наборов данных (следует использовать редко).


3. Лучшие практики для обработки обновлений в ClickHouse

Сценарий использованияРекомендуемый подход
Частые мелкие обновления❌ Не рекомендуется (ClickHouse только для добавлений)
Заменить старые строки✅ Использовать ReplacingMergeTree
Логические удаления✅ Использовать CollapsingMergeTree
Редкие удаления✅ Использовать ALTER TABLE DELETE
Редкие обновления✅ Использовать ALTER TABLE UPDATE
Быстрые обновления в аналитике в реальном времени✅ Использовать материализованные представления с AggregatingMergeTree

4. Резюме: Обработка обновлений и UPSERT операций в ClickHouse

ClickHouse не поддерживает in-place UPDATE / UPSERT, но есть обходные пути:
ReplacingMergeTree сохраняет последнюю версию строки для UPSERT операций.
CollapsingMergeTree обрабатывает логические удаления.
ALTER DELETE / ALTER UPDATE работает, но переписывает части (дорого).
✅ Используйте FINAL в запросах, чтобы немедленно применить дедупликацию.