Проекции
В этой статье рассматривается, что такое проекции, как их можно использовать и какие существуют варианты управления ими.
Обзор проекций
Проекции хранят данные в формате, который оптимизирует выполнение запросов. Этот механизм полезен для:
- Выполнения запросов по столбцу, который не является частью первичного ключа
- Предварительной агрегации столбцов, что снижает как вычислительные затраты, так и нагрузку на операции ввода-вывода (I/O)
Вы можете определить одну или несколько проекций для таблицы, и во время анализа запроса ClickHouse выберет проекцию с наименьшим объемом данных для сканирования, не изменяя запрос, заданный пользователем.
При использовании проекций внутренне создаётся новая скрытая таблица, что означает, что потребуется больше операций ввода-вывода и дискового пространства. Например, если в проекции определён первичный ключ, отличный от исходного, все данные из исходной таблицы будут продублированы.
Более технические подробности об устройстве проекций можно найти на этой странице.
Использование проекций
Пример фильтрации без использования первичного ключа
Создание таблицы:
С помощью команды ALTER TABLE можно добавить проекцию к существующей таблице:
Добавление данных:
Проекция позволит нам быстро фильтровать по user_name, даже если в исходной таблице user_name не был определён как PRIMARY_KEY.
Во время выполнения запроса ClickHouse определил, что при использовании проекции будет обработано меньше данных, так как данные упорядочены по user_name.
Чтобы убедиться, что запрос использует проекцию, мы можем просмотреть таблицу system.query_log. В столбце projections указано имя использованной проекции или пустое значение, если проекция не применялась:
Пример запроса предварительной агрегации
Создайте таблицу с проекцией projection_visits_by_user:
Добавьте данные:
Выполните первый запрос с GROUP BY по полю user_agent.
Этот запрос не будет использовать PROJECTION, так как предагрегация не соответствует.
Чтобы использовать проекцию, вы можете выполнять запросы, выбирающие часть или все поля предагрегации и поля GROUP BY:
Как уже упоминалось, вы можете просмотреть таблицу system.query_log, чтобы понять, использовалась ли проекция.
Поле projections показывает имя использованной проекции.
Оно будет пустым, если проекция не использовалась:
Обычная проекция с полем _part_offset
Создание таблицы с обычной проекцией, использующей поле _part_offset:
Добавление тестовых данных:
Использование _part_offset в качестве вторичного индекса
Поле _part_offset сохраняет свое значение при слияниях и мутациях, что делает его полезным для вторичного индексирования. Это можно использовать в запросах:
Управление проекциями
Доступны следующие операции с проекциями:
ДОБАВИТЬ ПРОЕКЦИЮ
Используйте приведённый ниже оператор, чтобы добавить в метаданные таблицы описание проекции:
Предложение WITH SETTINGS
WITH SETTINGS определяет настройки уровня проекции, которые задают, как проекция хранит данные (например, index_granularity или index_granularity_bytes).
Они напрямую соответствуют настройкам таблицы MergeTree, но применяются только к этой проекции.
Пример:
Настройки проекции переопределяют настройки таблицы, применяемые к этой проекции, с учётом правил проверки (например, недопустимые или несовместимые переопределения будут отклонены).
DROP PROJECTION
Используйте следующую команду, чтобы удалить описание проекции из метаданных таблицы и файлы проекции с диска. Эта операция реализована как мутация.
MATERIALIZE PROJECTION
Используйте следующую команду, чтобы перестроить проекцию name в партиции partition_name.
Это реализовано как мутация.
CLEAR PROJECTION
Используйте приведённую ниже команду, чтобы удалить файлы проекции с диска, не удаляя её описания. Эта операция реализована как мутация.
Команды ADD, DROP и CLEAR являются «лёгкими» в том смысле, что они только изменяют метаданные или удаляют файлы.
Кроме того, они реплицируются и синхронизируют метаданные проекций через ClickHouse Keeper или ZooKeeper.
Управление проекциями поддерживается только для таблиц с движком *MergeTree (включая реплицируемые варианты).
Управление поведением слияния проекций
Когда вы выполняете запрос, ClickHouse выбирает между чтением из исходной таблицы или одной из её проекций. Решение о чтении из исходной таблицы или одной из её проекций принимается отдельно для каждой части таблицы. В общем случае ClickHouse стремится прочитать как можно меньше данных и использует несколько приёмов, чтобы определить, из какой части читать, например, выполняет выборочное чтение значений первичного ключа части. В некоторых случаях у исходных частей таблицы могут отсутствовать соответствующие части проекций. Это может происходить, например, потому что создание проекции для таблицы в SQL по умолчанию является «ленивым» — оно влияет только на вновь вставляемые данные и не изменяет существующие части.
Поскольку одна из проекций уже содержит предварительно вычислённые агрегированные значения, ClickHouse старается читать из соответствующих частей проекций, чтобы избежать повторной агрегации во время выполнения запроса. Если у конкретной части отсутствует соответствующая часть проекции, выполнение запроса возвращается к исходной части.
Но что произойдёт, если строки в исходной таблице изменятся нетривиальным образом из‑за нетривиальных фоновых слияний частей данных?
Например, предположим, что таблица хранится с использованием движка ReplacingMergeTree.
Если одна и та же строка обнаружена в нескольких входных частях во время слияния, будет сохранена только самая новая версия строки (из самой недавно вставленной части), тогда как все более старые версии будут отброшены.
Аналогично, если таблица хранится с использованием движка AggregatingMergeTree, операция слияния может свернуть одни и те же строки во входных частях (на основе значений первичного ключа) в одну строку для обновления частичных состояний агрегации.
До ClickHouse v24.8 части проекций либо тихо рассинхронизировались с основными данными, либо некоторые операции, такие как обновления и удаления, вообще не могли выполняться, так как база данных автоматически выбрасывала исключение, если таблица имела проекции.
Начиная с v24.8, новый табличный параметр deduplicate_merge_projection_mode управляет поведением в случае упомянутых выше нетривиальных фоновых операций слияния, происходящих в частях исходной таблицы.
Мутации удаления — ещё один пример операций слияния частей, которые удаляют строки в частях исходной таблицы. Начиная с v24.7 у нас также есть параметр для управления поведением в отношении мутаций удаления, запускаемых легковесным удалением: lightweight_mutation_projection_mode.
Ниже перечислены возможные значения как для deduplicate_merge_projection_mode, так и для lightweight_mutation_projection_mode:
throw(по умолчанию): Выбрасывается исключение, предотвращающее рассинхронизацию частей проекций.drop: Затронутые части таблицы проекций удаляются. Для этих частей запросы будут выполняться по исходной части таблицы.rebuild: Затронутая часть проекции перестраивается, чтобы оставаться согласованной с данными в исходной части таблицы.