CollapsingMergeTree
Description
Движок CollapsingMergeTree
унаследован от MergeTree и добавляет логику для сжатия строк в процессе объединения. Движок таблицы CollapsingMergeTree
асинхронно удаляет (сжимает) пары строк, если все поля в ключе сортировки (ORDER BY
) эквивалентны, за исключением специального поля Sign
, которое может иметь значения 1
или -1
. Строки без пары строк с противоположным значением Sign
сохраняются.
Для получения более подробной информации см. раздел Сжатие документа.
Этот движок может значительно уменьшить объем хранения, что, в свою очередь, увеличивает эффективность запросов SELECT
.
Parameters
Все параметры этого движка таблицы, за исключением параметра Sign
, имеют то же значение, что и в MergeTree
.
Sign
— Название, данное столбцу с типом строки, где1
— это строка "состояния", а-1
— строка "отмены". Тип: Int8.
Creating a Table
Устаревший метод создания таблицы
Метод ниже не рекомендуется для использования в новых проектах. Мы советуем, по возможности, обновить старые проекты, чтобы использовать новый метод.
Sign
— Название, данное столбцу с типом строки, где 1
— это строка "состояния", а -1
— строка "отмены". Int8.
- Для описания параметров запроса см. описание запроса.
- При создании таблицы
CollapsingMergeTree
требуются те же условия запроса, как и при создании таблицыMergeTree
.
Collapsing
Data
Рассмотрим ситуацию, когда необходимо сохранить постоянно изменяющиеся данные для некоторого объекта. Может показаться логичным иметь одну строку на объект и обновлять ее всякий раз, когда что-то меняется, однако операции обновления дороги и медлительны для СУБД, поскольку требуют переписывания данных в хранилище. Если нам нужно быстро записывать данные, выполнение большого количества обновлений не является приемлемым подходом, но мы всегда можем последовательно записывать изменения объекта. Для этого мы используем специальный столбец Sign
.
- Если
Sign
=1
, это означает, что строка является строкой "состояния": строка, содержащая поля, которые представляют текущее допустимое состояние. - Если
Sign
=-1
, это означает, что строка является строкой "отмены": строка, используемая для отмены состояния объекта с такими же атрибутами.
Например, мы хотим подсчитать, сколько страниц пользователи открыли на каком-либо веб-сайте и как долго они на них находились. В некоторый момент времени мы записываем следующую строку с состоянием активности пользователя:
Позже мы регистрируем изменение активности пользователя и записываем это с помощью следующих двух строк:
Первая строка отменяет предыдущее состояние объекта (в данном случае представляющее пользователя). Она должна копировать все поля ключа сортировки для "отмененной" строки, кроме Sign
. Вторая строка содержит текущее состояние.
Поскольку нам нужно только последнее состояние активности пользователя, оригинальную строку "состояния" и строку "отмены", которую мы вставили, можно удалить, как показано ниже, сжимая недействительное (старое) состояние объекта:
CollapsingMergeTree
выполняет именно такое сжатие в процессе объединения частей данных.
Причина, по которой для каждого изменения требуется две строки, обсуждается в параграфе Алгоритм.
Особенности такого подхода
- Программа, которая записывает данные, должна помнить состояние объекта, чтобы быть в состоянии отменить его. Строка "отмены" должна содержать копии полей ключа сортировки "состояния" и противоположный
Sign
. Это увеличивает начальный размер хранения, но позволяет нам быстро записывать данные. - Долгие растущие массивы в столбцах снижают эффективность движка из-за увеличенной нагрузки на запись. Чем проще данные, тем выше эффективность.
- Результаты
SELECT
сильно зависят от согласованности истории изменений объекта. Будьте внимательны при подготовке данных для вставки. Вы можете получить непредсказуемые результаты с неконсистентными данными. Например, отрицательные значения для неотрицательных метрик, таких как глубина сессии.
Algorithm
Когда 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 возвращается только последняя строка состояния для каждого ключа.
Examples
Example of Use
Учитывая следующий пример данных:
Давайте создадим таблицу UAct
, используя CollapsingMergeTree
:
Затем мы вставим некоторые данные:
Мы используем два запроса INSERT
, чтобы создать две разные части данных.
Если мы вставляем данные с помощью одного запроса, ClickHouse создаст только одну часть данных и никогда не выполнит слияние.
Мы можем выбрать данные, используя:
Давайте посмотрим на возвращенные данные выше и посмотрим, произошло ли сжатие... С помощью двух запросов INSERT
мы создали две части данных. Запрос SELECT
был выполнен в двух потоках, и мы получили случайный порядок строк. Однако сжатие не произошло, потому что объединения частей данных еще не произошло, а ClickHouse сливает части данных в фоновом режиме в неизвестный момент, который мы не можем предсказать.
Следовательно, нам необходимо провести агрегацию, которую мы выполняем с помощью агрегатной функции sum
и условия HAVING
:
Если нам не нужна агрегация и мы хотим принудительно выполнить сжатие, мы также можем использовать модификатор FINAL
для условия FROM
.
Этот способ выборки данных менее эффективен и не рекомендуется для использования с большими объемами сканируемых данных (миллионы строк).
Example of Another Approach
Идея этого подхода состоит в том, что слияния учитывают только ключевые поля. В строке "отмены" мы можем, следовательно, указать отрицательные значения, которые выравнивают предыдущую версию строки при суммировании без использования столбца Sign
.
В этом примере мы воспользуемся следующими демонстрационными данными:
Для этого подхода необходимо изменить типы данных PageViews
и Duration
, чтобы хранить отрицательные значения. Мы поэтому изменяем значения этих столбцов с UInt8
на Int16
, когда создаем нашу таблицу UAct
, используя CollapsingMergeTree
:
Давайте протестируем подход, вставляя данные в нашу таблицу.
Для примеров или маленьких таблиц это, тем не менее, приемлемо: