Перейти к основному содержимому
Перейти к основному содержимому

CollapsingMergeTree

Описание

Движок CollapsingMergeTree унаследован от MergeTree и добавляет логику для сжатия строк во время процесса слияния. Движок таблиц CollapsingMergeTree асинхронно удаляет (сжимает) пары строк, если все поля в ключе сортировки (ORDER BY) эквивалентны, за исключением специального поля Sign, которое может принимать значения 1 или -1. Строки без пары строк с противоположным значением Sign сохраняются.

С дополнительной информацией можно ознакомиться в разделе Сжатие документа.

примечание

Этот движок может значительно уменьшить объем хранилища, увеличивая, как следствие, эффективность запросов SELECT.

Параметры

Все параметры этого движка таблицы, за исключением параметра Sign, имеют то же значение, что и в MergeTree.

  • Sign — Имя, данное колонке с типом строки, где 1 — это строка "состояния", а -1 — строка "отмены". Тип: Int8.

Создание таблицы

Устаревший метод создания таблицы
примечание

Метод ниже не рекомендуется для использования в новых проектах. Мы советуем, по возможности, обновить старые проекты для использования нового метода.

Sign — Имя, данное колонке с типом строки, где 1 — это строка "состояния", а -1 — строка "отмены". Int8.

Сжатие

Данные

Рассмотрим ситуацию, когда необходимо сохранять постоянно изменяющиеся данные для определенного объекта. Может показаться логичным иметь одну строку на объект и обновлять её всякий раз, когда что-то меняется, однако операции обновления дороги и медлительны для СУБД, так как они требуют перезаписи данных в хранилище. Если нужно быстро записывать данные, выполнение большого количества обновлений не является приемлемым подходом, но мы всегда можем записывать изменения объекта последовательно. Для этого мы используем специальную колонку Sign.

  • Если Sign = 1, это означает, что строка является строкой "состояния": строка, содержащая поля, которые представляют текущее действительное состояние.
  • Если Sign = -1, это означает, что строка является строкой "отмены": строка, используемая для отмены состояния объекта с теми же атрибутами.

Например, мы хотим рассчитать, сколько страниц пользователи посмотрели на неком сайте и как долго они их посещали. В какой-то момент времени мы записываем следующую строку с состоянием активности пользователя:

Позже, мы фиксируем изменение активности пользователя и записываем его с двумя следующими строками:

Первая строка отменяет предыдущее состояние объекта (представляет собой пользователя в данном случае). Она должна копировать все поля ключа сортировки для "отмененной" строки, кроме Sign. Вторая строка выше содержит текущее состояние.

Поскольку нам нужно только последнее состояние активности пользователя, исходную строку "состояния" и строку "отмены", которую мы вставили, можно удалить, как показано ниже, сжимая недействительное (старое) состояние объекта:

CollapsingMergeTree выполняет именно такое сжатие при слиянии частей данных.

примечание

Причина, по которой нужны две строки для каждого изменения, обсуждается более подробно в разделе Алгоритм.

Особенности такого подхода

  1. Программа, которая записывает данные, должна помнить о состоянии объекта, чтобы иметь возможность отменить его. Строка "отмены" должна содержать копии полей ключа сортировки строки "состояния" и противоположный Sign. Это увеличивает начальный размер хранилища, но позволяет быстро записывать данные.
  2. Долго растущие массивы в колонках снижают эффективность движка из-за увеличенной нагрузки при записи. Чем проще данные, тем выше эффективность.
  3. Результаты 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:

Давайте протестируем подход, вставив данные в нашу таблицу.

Для примеров или маленьких таблиц это, хоть и допустимо: