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
.
Это очень неэффективный способ выбора данных. Не используйте его для больших таблиц.