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 в запросах, чтобы немедленно применить дедупликацию.