VersionedCollapsingMergeTree
Этот движок:
- Позволяет быстро записывать состояния объектов, которые постоянно меняются.
- Удаляет старые состояния объектов в фоновом режиме. Это значительно уменьшает объем хранения.
Смотрите раздел Collapsing для подробностей.
Движок наследуется от MergeTree и добавляет логику уменьшения строк к алгоритму объединения частей данных. VersionedCollapsingMergeTree
служит той же цели, что и CollapsingMergeTree, но использует другой алгоритм коллапсирования, который позволяет вставлять данные в любом порядке с несколькими потоками. В частности, колонка Version
помогает правильно коллапсировать строки, даже если они были вставлены в неправильном порядке. Напротив, CollapsingMergeTree
позволяет только строго последовательную вставку.
Создание таблицы
Для описания параметров запросов смотрите описание запроса.
Параметры движка
Параметр | Описание | Тип |
---|---|---|
sign | Название колонки с типом строки: 1 — это строка "состояния", -1 — это строка "отмены". | Int8 |
version | Название колонки с версией состояния объекта. | Int* , UInt* , Date , Date32 , DateTime или DateTime64 |
Классы запросов
При создании таблицы VersionedCollapsingMergeTree
требуются те же классы, что и при создании таблицы MergeTree
.
Устаревший метод создания таблицы
Не используйте этот метод в новых проектах. По возможности, переключите старые проекты на описанный выше метод.
Все параметры, кроме sign
и version
, имеют то же значение, что и в MergeTree
.
-
sign
— Название колонки с типом строки:1
— это строка "состояния",-1
— это строка "отмены".Тип данных колонки —
Int8
. -
version
— Название колонки с версией состояния объекта.Тип данных колонки должен быть
UInt*
.
Коллапсирование
Данные
Рассмотрим ситуацию, когда необходимо сохранить постоянно меняющиеся данные для какого-то объекта. Уместно иметь одну строку для объекта и обновлять её всякий раз, когда происходят изменения. Однако операция обновления дорогая и медленная для СУБД, поскольку требует переписывания данных в хранилище. Обновление недопустимо, если необходимо быстро записывать данные, но вы можете последовательно записывать изменения объекта следующим образом.
Используйте колонку Sign
при записи строки. Если Sign = 1
, это означает, что строка — это состояние объекта (назовем его "строкой состояния"). Если Sign = -1
, это указывает на отмену состояния объекта с теми же атрибутами (назовем его "строкой отмены"). Также используйте колонку Version
, которая должна идентифицировать каждое состояние объекта отдельным номером.
Например, мы хотим подсчитать, сколько страниц пользователи посетили на каком-то сайте и сколько времени они там были. В какой-то момент времени мы пишем следующую строку с состоянием активности пользователя:
В какой-то момент позже мы регистрируем изменение активности пользователя и записываем это двумя следующими строками.
Первая строка отменяет предыдущее состояние объекта (пользователя). Она должна копировать все поля отмененного состояния, кроме Sign
.
Вторая строка содержит текущее состояние.
Поскольку нам нужно только последнее состояние активности пользователя, строки
могут быть удалены, коллапсируя недействительное (старое) состояние объекта. VersionedCollapsingMergeTree
делает это во время объединения частей данных.
Чтобы узнать, почему нам нужно две строки для каждого изменения, смотрите Алгоритм.
Примечания по использованию
- Программа, записывающая данные, должна помнить состояние объекта, чтобы иметь возможность его отменить. Строка "отмена" должна содержать копии полей первичного ключа и версию строки "состояния" и противоположный
Sign
. Это увеличивает начальный размер хранения, но позволяет быстро записывать данные. - Длинные растущие массивы в колонках снижают эффективность движка из-за нагрузки на запись. Чем проще данные, тем лучше эффективность.
- Результаты
SELECT
сильно зависят от последовательности изменений объекта. Будьте внимательны при подготовке данных для вставки. Вы можете получить непредсказуемые результаты с несогласованными данными, например, отрицательные значения для неотрицательных метрик, таких как глубина сессии.
Алгоритм
Когда ClickHouse объединяет части данных, он удаляет каждую пару строк, которые имеют одинаковый первичный ключ и версию, и разные Sign
. Порядок строк не важен.
Когда ClickHouse вставляет данные, он упорядочивает строки по первичному ключу. Если колонка Version
не находится в первичном ключе, ClickHouse добавляет её в первичный ключ неявно как последнее поле и использует её для упорядочивания.
Выбор данных
ClickHouse не гарантирует, что все строки с одинаковым первичным ключом будут в одной конечной части данных или даже на одном физическом сервере. Это справедливо как для записи данных, так и для последующего объединения частей данных. Кроме того, ClickHouse обрабатывает запросы SELECT
с несколькими потоками, и он не может предсказать порядок строк в результате. Это означает, что требуется агрегация, если необходимо получить полностью "коллапсированные" данные из таблицы VersionedCollapsingMergeTree
.
Чтобы завершить коллапсирование, напишите запрос с оператором GROUP BY
и агрегатными функциями, которые учитывают знак. Например, для подсчета количества используйте sum(Sign)
вместо count()
. Чтобы вычислить сумму чего-либо, используйте sum(Sign * x)
вместо sum(x)
, и добавьте HAVING sum(Sign) > 0
.
Агрегаты count
, sum
и avg
могут быть вычислены таким образом. Агрегат uniq
может быть вычислен, если объект имеет хотя бы одно неколлапсированное состояние. Агрегаты min
и max
нельзя вычислить, потому что VersionedCollapsingMergeTree
не сохраняет историю значений коллапсированных состояний.
Если вам нужно извлечь данные с "коллапсированием", но без агрегации (например, чтобы проверить, есть ли строки с новыми значениями, соответствующими определенным условиям), вы можете использовать модификатор FINAL
для оператора FROM
. Этот подход неэффективен и не должен использоваться с большими таблицами.
Пример использования
Пример данных:
Создание таблицы:
Вставка данных:
Мы используем два запроса INSERT
, чтобы создать две разные части данных. Если мы вставим данные с одним запросом, ClickHouse создаст одну часть данных и никогда не выполнит слияние.
Получение данных:
Что мы здесь видим и где коллапсированные части?
Мы создали две части данных, используя два запроса INSERT
. Запрос SELECT
был выполнен в два потока, и результат — случайный порядок строк.
Коллапсирование не произошло, поскольку части данных еще не были объединены. ClickHouse объединяет части данных в неизвестный момент времени, который мы не можем предсказать.
Вот почему нам нужна агрегация:
Если нам не нужна агрегация и мы хотим принудительно коллапсировать, мы можем использовать модификатор FINAL
для оператора FROM
.
Это очень неэффективный способ выбора данных. Не используйте его для больших таблиц.