ReplacingMergeTree хранит поступающие версии, а при слиянии частей оставляет одну строку для одинакового ORDER BY. Он подходит для аналитической копии последнего состояния сущности. Это не ограничение уникальности и не немедленный UPDATE по ключу.
Идентичность и версия
Рассмотрим состояние сущности: стабильный id, возрастающая версия и изменяемые поля.
CREATE TABLE entity_state
(
id UInt64,
version UInt64,
state String,
value UInt64
)
ENGINE = ReplacingMergeTree(version)
ORDER BY id;Строки (7, 1, 'new', 100) и (7, 2, 'ready', 120) описывают одну сущность. При схлопывании выигрывает версия 2. Версия задаёт порядок состояний; время поступления может отличаться от порядка изменений. Если две строки имеют одинаковую максимальную версию, нельзя использовать эту ситуацию как надёжное правило выбора актуального бизнес-состояния.
ORDER BY должен оставаться одинаковым между версиями. Добавление изменяемого state в ключ сделает new и ready разными ключами, и строки не схлопнутся. Версию также не включают в ключ, когда требуется замена по id.
Без аргумента version движок выбирает строку по порядку частей и вставок. Для изменений, доставляемых с задержками, задают явную версию источника.
Что видно до слияния
Обычный SELECT может вернуть обе версии: merge работает в фоне и не обязан немедленно обработать все части. Запрос с FINAL выполняет необходимое схлопывание во время чтения:
SELECT id, state, value
FROM entity_state FINAL;FINAL учитывает семантику движка, но не переписывает данные на диске. Он добавляет работу к запросу; стоимость зависит от версии ClickHouse, ключа, фильтра и объёма выбранных частей. OPTIMIZE FINAL — отдельная тяжёлая операция с частями, а не обязательный шаг после каждой вставки.
Альтернатива для явно версионных данных — выбрать значения по максимальной версии:
SELECT id, argMax(tuple(state, value), version) AS latest
FROM entity_state
GROUP BY id;Tuple связывает атрибуты одной выбранной строки. Несколько отдельных argMax при равных версиях или NULL требуют особенно внимательной семантики. Уникальный порядок версий лучше определить в источнике.
Партиции и удаления
Parts разных партиций не сливаются. Все версии одной сущности желательно направлять в одну стабильную партицию. Если партиция вычисляется из изменяемого поля, версии могут разойтись по разным группам. Поведение FINAL между партициями дополнительно зависит от настроек; это не замена правильной модели.
Вариант ReplacingMergeTree(version, is_deleted) позволяет передавать признак удаления в последней версии. Удаляющую версию нельзя бездумно физически выбросить: поздно пришедшая старая строка способна снова появиться. Очистка требует гарантии, что старые версии больше не поступят, и согласованного состояния реплик.
Поиск и устранение повторов
Сначала определяют, что считается дублем: полностью одинаковые строки или несколько версий одного бизнес-ключа. Для поиска полных повторов в entity_state можно сгруппировать все её поля:
SELECT id, version, state, value, count() AS copies
FROM entity_state
GROUP BY id, version, state, value
HAVING copies > 1;Разные версии одного id описывают состояния сущности; для выбора последнего нужна версия. Удаление всех строк по повторяющемуся id уничтожит и экземпляр, который требовалось сохранить.
При повторной доставке нужны устойчивый идентификатор и версия источника. Для пересчёта можно сохранять вход в промежуточной таблице, выбирать однозначное состояние и затем публиковать результат. Границу обработанного входа согласуют с поступлением новых данных; частоту пересчёта выбирают по допустимой задержке и стоимости.
Последнее состояние и агрегаты
Для журнала независимых событий потеря старых строк может быть нежелательна; тогда обычный MergeTree сохраняет историю. Replacing выбирают, когда действительно нужна одна актуальная сущность.
Инкрементальная materialized view обрабатывает каждую вставку. Она может уже учесть и старую, и новую версию. Последующее схлопывание источника не отменяет прежний вклад в витрину. Для корректной агрегации нужен подготовленный вход без повторного вклада, модель изменений со знаком или согласованный пересчёт.