CollapsingMergeTree
Описание
Двигатель CollapsingMergeTree
наследуется от MergeTree и добавляет логику для коллапса строк в процессе слияния. Двигатель таблицы CollapsingMergeTree
асинхронно удаляет (коллапсирует) пары строк, если все поля в ключе сортировки (ORDER BY
) эквивалентны, за исключением специального поля Sign
, которое может принимать значения 1
или -1
. Строки без пары с противоположным значением Sign
остаются.
Для получения более подробной информации смотрите раздел Collapsing документа.
Этот движок может значительно сократить объем хранения, что, в свою очередь, повысит эффективность запросов SELECT
.
Параметры
Все параметры этого движка таблицы, за исключением параметра Sign
, имеют то же значение, что и в MergeTree
.
Sign
— Имя столбца с типом строки, где1
соответствует строке "состояния", а-1
соответствует строке "отмены". Тип: Int8.
Создание таблицы
Устаревший метод создания таблицы
Метод, описанный ниже, не рекомендуется для использования в новых проектах. Мы советуем, по возможности, обновить старые проекты для использования нового метода.
Sign
— Имя столбца с типом строки, где 1
соответствует строке "состояния", а -1
соответствует строке "отмены". Int8.
- Для описания параметров запросов смотрите описание запроса.
- При создании таблицы
CollapsingMergeTree
требуются те же клаузы запроса, что и при создании таблицыMergeTree
.
Коллапс
Данные
Рассмотрим ситуацию, когда необходимо сохранять постоянно изменяющиеся данные для некоего объекта. Может показаться логичным иметь одну строку на объект и обновлять ее каждый раз, когда что-то меняется, однако операции обновления дороги и медленны для СУБД, так как они требуют перезаписи данных в хранилище. Если мы хотим быстро записывать данные, выполнение большого количества обновлений не является приемлемым подходом, но мы всегда можем записывать изменения объекта последовательно. Для этого мы используем специальный столбец Sign
.
- Если
Sign
=1
, это означает, что строка является строкой "состояния": строка, содержащая поля, которые представляют текущее корректное состояние. - Если
Sign
=-1
, это означает, что строка является строкой "отмены": строка, используемая для отмены состояния объекта с теми же атрибутами.
Например, мы хотим подсчитать, сколько страниц пользователи просмотрели на каком-то веб-сайте и как долго они находились на них. В определенный момент времени мы записываем следующую строку с состоянием активности пользователей:
В более поздний момент времени мы регистрируем изменение активности пользователя и записываем его с помощью следующих двух строк:
Первая строка отменяет предыдущее состояние объекта (представляющего данного пользователя). Она должна дублировать все поля ключа сортировки для "отмененной" строки, кроме Sign
. Вторая строка выше содержит текущее состояние.
Поскольку нам нужно лишь последнее состояние активности пользователя, мы можем удалить оригинальную строку "состояния" и строку "отмены", как показано ниже, коллапсируя недействительное (старое) состояние объекта:
CollapsingMergeTree
выполняет именно такое коллапсирование во время слияния частей данных.
Причина, по которой две строки необходимы для каждого изменения, обсуждается в пункте Алгоритм.
Особенности такого подхода
- Программа, записывающая данные, должна помнить состояние объекта, чтобы иметь возможность его отменить. Строка "отмены" должна содержать копии полей ключа сортировки строки "состояния" и противоположный
Sign
. Это увеличивает первоначальный размер хранения, но позволяет быстро записывать данные. - Долгие растущие массивы в столбцах снижают эффективность движка из-за увеличенной нагрузки при записи. Чем проще данные, тем выше эффективность.
- Результаты
SELECT
сильно зависят от согласованности истории изменений объекта. Будьте осторожны при подготовке данных для вставки. Вы можете получить непредсказуемые результаты при несогласованных данных. Например, отрицательные значения для неотрицательных метрик, таких как глубина сессии.
Алгоритм
Когда ClickHouse объединяет части данных, каждая группа последовательных строк с одним и тем же ключом сортировки (ORDER BY
) сокращается до не более чем двух строк: строка "состояния" с Sign
= 1
и строка "отмены" с Sign
= -1
. Другими словами, в ClickHouse записи коллапсируются.
Для каждой полученной части данных ClickHouse сохраняет:
1. | Первая строка "отмены" и последняя строка "состояния", если количество строк "состояния" и "отмены" совпадает и последняя строка является строкой "состояния". |
2. | Последняя строка "состояния", если строк "состояния" больше, чем строк "отмены". |
3. | Первая строка "отмены", если строк "отмены" больше, чем строк "состояния". |
4. | Ни одной из строк, в остальных случаях. |
Кроме того, когда количество строк "состояния" на две и более превышает количество строк "отмены", или наоборот, слияние продолжается. Однако ClickHouse рассматривает эту ситуацию как логическую ошибку и записывает ее в журнал сервера. Эта ошибка может возникнуть, если одни и те же данные вставляются более одного раза. Таким образом, коллапсирование не должно изменять результаты расчета статистики. Изменения постепенно коллапсируются, так что в конце остается только последнее состояние почти каждого объекта.
Столбец Sign
обязателен, потому что алгоритм слияния не гарантирует, что все строки с одним и тем же ключом сортировки будут находиться в одной и той же результирующей части данных и даже на одном физическом сервере. ClickHouse обрабатывает запросы SELECT
с использованием нескольких потоков, и он не может предсказать порядок строк в результате.
Агрегация необходима, если требуется получить полностью "коллапсированные" данные из таблицы CollapsingMergeTree
. Для завершения коллапсирования запишите запрос с клаузой GROUP BY
и агрегатными функциями, которые учитывают знак. Например, чтобы подсчитать количество, используйте sum(Sign)
вместо count()
. Чтобы подсчитать сумму чего-либо, используйте sum(Sign * x)
с HAVING sum(Sign) > 0
вместо sum(x)
так, как показано в примере ниже.
Агрегаты count
, sum
и avg
можно вычислить таким образом. Агрегат uniq
можно вычислить, если у объекта есть хотя бы одно неколлапсированное состояние. Агрегаты min
и max
нельзя вычислить, так как CollapsingMergeTree
не сохраняет историю коллапсированных состояний.
Если вам нужно извлечь данные без агрегации (например, чтобы проверить, есть ли строки, значения которых соответствуют определенным условиям),
вы можете использовать модификатор FINAL
для клаузи FROM
. Он объединит данные перед возвратом результата.
Для CollapsingMergeTree возвращается только последняя строка состояния для каждого ключа.
Примеры
Пример использования
При наличии следующих примерных данных:
Создадим таблицу UAct
, используя CollapsingMergeTree
:
Далее мы вставим некоторые данные:
Мы используем два запроса INSERT
, чтобы создать две разные части данных.
Если мы вставим данные одним запросом, ClickHouse создаст только одну часть данных и не выполнит никаких слияний.
Мы можем выбрать данные с помощью:
Давайте взглянем на возвращенные данные и посмотрим, произошло ли коллапсирование...
С помощью двух запросов INSERT
мы создали две части данных. Запрос SELECT
был выполнен в два потока, и мы получили случайный порядок строк. Однако коллапсирование не произошло, потому что слияния частей данных еще не было, и ClickHouse объединяет части данных в фоновом режиме в неизвестный момент времени, который мы не можем предсказать.
Следовательно, нам нужна агрегация, которую мы выполняем с помощью агрегатной функции sum
и клаузи HAVING
:
Если нам не нужна агрегация и мы хотим принудительное коллапсирование, мы также можем использовать модификатор FINAL
для клаузи FROM
.
Этот способ выборки данных менее эффективен и не рекомендуется для использования с большими объемами сканируемых данных (миллионы строк).
Пример другого подхода
Идея этого подхода заключается в том, что слияния учитывают только ключевые поля. В строке "отмены" мы можем, следовательно, указывать отрицательные значения, которые уравнивают предыдущую версию строки при суммировании без использования столбца Sign
.
Для этого примера мы воспользуемся приведенными ниже примерными данными:
Для этого подхода необходимо изменить типы данных PageViews
и Duration
, чтобы хранить отрицательные значения. Поэтому мы изменяем значения этих столбцов с UInt8
на Int16
, когда создаем нашу таблицу UAct
, используя collapsingMergeTree
:
Давайте протестируем подход, вставив данные в нашу таблицу.
Для примеров или небольших таблиц это, однако, приемлемо: