В уроке про MergeTree мы видели, что части сливаются в фоне. Обычный MergeTree при слиянии просто складывает строки вместе. Его родственники пользуются слиянием, чтобы заодно навести порядок в данных: выбросить старые версии строки, сложить суммы, погасить отменённые записи. Разберём, зачем это нужно и в чём подвох.
Задача: последнее состояние
У каждого курьера «Гонца» есть профиль: зона, транспорт. Курьер переехал в другую зону, профиль поменялся. В PostgreSQL мы бы написали UPDATE. В ClickHouse правка строки дорогая: она переписывает целую часть. Зато дописать новую версию строки дёшево. Пусть таблица хранит все версии, а лишние убирает сама.
Так работает ReplacingMergeTree:
CREATE TABLE couriers (
id UInt32,
zone String,
transport String,
updated_at DateTime
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY id;
Строки с одинаковым ключом ORDER BY (здесь id) считаются версиями одного объекта. При слиянии от них остаётся одна, с наибольшим updated_at.
Подвох: слияние когда-нибудь
Слияние идёт в фоне, когда сервер сочтёт нужным. До этого старые и новые версии лежат в разных частях, и обычный SELECT увидит обе. ReplacingMergeTree обещает, что дубли когда-нибудь исчезнут, но не обещает когда. Некоторые дубли не исчезнут никогда: части разных партиций не сливаются.
Поэтому запросы к таким таблицам пишут так, чтобы ответ был верным и до слияния. Есть два способа:
- слово FINAL после имени таблицы:
SELECT * FROM couriers FINAL. ClickHouse схлопнет версии прямо при чтении. Ответ верный, но запрос тяжелее; - агрегат
argMax:SELECT id, argMax(zone, updated_at) FROM couriers GROUP BY id. Для каждого курьера берётся зона из строки с наибольшимupdated_at.
Посмотри сам
Курьер 1 переехал из Центра в ВИЗ. Смотрим таблицу без FINAL, с FINAL и после принудительного слияния.
До слияния обычный запрос вернул три строки: две версии курьера 1. FINAL и argMax дали правильный ответ сразу. После OPTIMIZE старая версия исчезла физически.
урок из курса
Урок из курса «ClickHouse и аналитические базы». Курс покупается один раз и остаётся навсегда: 500 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.