ClickHouse UPDATE 2026: мутации, ReplacingMergeTree и новый Lightweight UPDATE в 25.8

Многие приходят в ClickHouse из классических баз типа Postgres и поэтому очень хочется написать привычный UPDATE. В ClickHouse это очень плохая идея! База начинает тормозить, фоновые процессы не успевают за нагрузкой, и в итоге всё встаёт.

ClickHouse UPDATE 2026: мутации, ReplacingMergeTree и новый Lightweight UPDATE в 25.8

Проблема не в ClickHouse — проблема в модели мышления. ClickHouse создан для быстрой пакетной записи и аналитики на миллиардах строк, а не для точечного изменения одной ячейки. В этой статье разберём, как правильно работать с обновлениями.

Почему классический UPDATE — это боль

В ClickHouse стандартный ALTER TABLE ... UPDATE работает через мутации — асинхронные фоновые процессы. База не меняет значение на месте: она физически переписывает целые куски данных (data parts) на диске. Хотите обновить статус у ста пользователей — база перелопатит гигабайты данных.

Если мутаций много, они выстраиваются в очередь. Очередь растёт быстрее, чем успевает обрабатываться. База встаёт.

Способ 1. ReplacingMergeTree — стандарт для upsert

Самый распространённый паттерн. Вместо обновления старой строки вы вставляете новую версию с тем же ключом. ClickHouse схлопывает дубли при фоновом мерже, оставляя последнюю по версии строку.

CREATE TABLE orders ( order_id UInt64, status String, updated_at DateTime ) ENGINE = ReplacingMergeTree(updated_at) ORDER BY order_id;

Способ 2. Append-only + argMax — когда важна история

Вместо того чтобы менять строку, вы просто дописываете новую. Текущее состояние вычисляете при чтении через argMax:

SELECT order_id, argMax(status, updated_at) AS current_status FROM order_events GROUP BY order_id;

Никаких мутаций, никаких фоновых процессов. Запись максимально быстрая, история полностью сохранена.

Подходит для: event-sourcing, аудит-логов, потоков где важно знать не только «что сейчас», но и «как менялось».

Способ 3. CollapsingMergeTree — для потоковых изменений

Работает через колонку Sign: строка с Sign = 1 актуальна, Sign = -1 отменяет предыдущую версию. При мерже база схлопывает пары и оставляет только актуальное состояние.

CREATE TABLE user_activity ( user_id UInt64, score Float32, Sign Int8 ) ENGINE = CollapsingMergeTree(Sign) ORDER BY user_id; -- Отменяем старое значение INSERT INTO user_activity VALUES (42, 10.0, -1); -- Вставляем новое INSERT INTO user_activity VALUES (42, 15.0, 1);

Сложнее в реализации, чем ReplacingMergeTree, но очень эффективно для высокочастотных изменений — например, обновления рейтингов или балансов в реальном времени.

Способ 4. Lightweight UPDATE (с версии 25.7) — наконец-то как в SQL

В релизе 25.7 ClickHouse добавил стандартный SQL-синтаксис UPDATE, основанный на новом механизме patch parts. В версии 25.8 фича получила статус Beta и включена по умолчанию.

UPDATE orders SET status = 'shipped' WHERE order_id = 12345;

Вместо перезаписи целых колонок, как это делают классические мутации, Lightweight UPDATE записывает только изменённые значения в небольшие patch parts. Патч-парты применяются при чтении сразу и объединяются с основными данными в фоне при мерже.

Новый синтаксис UPDATE хорошо работает для частых изменений, затрагивающих примерно 10% таблицы или меньше. Для масштабных обновлений, после которых нужна максимальная производительность на чтение, классические мутации через ALTER TABLE по-прежнему предпочтительнее.

По бенчмаркам самого ClickHouse — ускорение до 1000× по сравнению с классическими мутациями для точечных обновлений.

Как выбрать подход

ClickHouse UPDATE 2026: мутации, ReplacingMergeTree и новый Lightweight UPDATE в 25.8

Если этот разбор ClickHouse показался вам полезным, но вы чувствуете, что в знаниях есть пробелы — загляните в мой Roadmap для Data Engineer. Там я собрал пошаговый план: что учить и в каком порядке, чтобы вырасти из Junior до Senior.

А если хотите больше живых кейсов, разборов с собеседований и советов по карьере в DE — заходите в мой Telegram-канале.