Перейти к основному содержанию
Индексы пропуска данных стоит рассматривать после того, как уже выполнены основные рекомендации: оптимизированы типы, выбран подходящий первичный ключ и использованы materialized views. Если вы только начинаете знакомиться с индексами пропуска данных, хорошей отправной точкой будет это руководство. Эти типы индексов могут ускорить выполнение запросов, если применять их осознанно и понимать, как именно они работают. ClickHouse предоставляет мощный механизм под названием индексы пропуска данных, который может значительно сократить объем данных, сканируемых при выполнении запроса, — особенно когда первичный ключ не помогает для конкретного условия фильтрации. В отличие от традиционных баз данных, которые опираются на вторичные индексы на уровне строк (например, B-деревья), ClickHouse — это столбцовая СУБД и не хранит расположение строк в формате, который позволял бы использовать такие структуры. Вместо этого он использует индексы пропуска данных, которые помогают избегать чтения блоков данных, заведомо не соответствующих условиям фильтрации запроса. Индексы пропуска данных работают за счет хранения метаданных о блоках данных — например, минимальных/максимальных значений, наборов значений или представлений Bloom filter — и использования этих метаданных во время выполнения запроса, чтобы определить, какие блоки данных можно полностью пропустить. Они применимы только к семейству движков таблиц MergeTree и определяются с помощью выражения, типа индекса, имени и гранулярности, задающей размер каждого индексируемого блока. Эти индексы хранятся рядом с данными таблицы и используются, когда фильтр запроса соответствует выражению индекса. Существует несколько типов индексов пропуска данных, каждый из которых подходит для разных типов запросов и распределений данных:
  • minmax: Отслеживает минимальное и максимальное значение выражения для каждого блока. Идеально подходит для диапазонных запросов по слабо отсортированным данным.
  • set(N): Отслеживает набор значений до указанного размера N для каждого блока. Эффективен для столбцов с низкой мощностью в пределах блоков.
  • text: Строит инвертированный индекс по токенизированным строковым данным, обеспечивая эффективный и детерминированный полнотекстовый поиск. Рекомендуется для текстов на естественном языке или больших столбцов с произвольным текстом, где требуются точный поиск по токенам и масштабируемый поиск по нескольким терминам, вместо приближенных подходов на основе Bloom filter.
  • bloom_filter: Вероятностно определяет, присутствует ли значение в блоке, что позволяет быстро выполнять приближенную фильтрацию на принадлежность множеству. Эффективен для оптимизации запросов вида «иголка в стоге сена», когда требуется найти положительное совпадение.
  • tokenbf_v1 / ngrambf_v1: (Устарело) Специализированные варианты Bloom filter, предназначенные для поиска токенов или последовательностей символов в строках — особенно полезны для данных логов или сценариев текстового поиска. Устарели в версиях ClickHouse >= 26.2 в пользу текстовых индексов.
Несмотря на свою мощность, индексы пропуска данных нужно использовать с осторожностью. Они приносят пользу только тогда, когда позволяют исключить значимое количество блоков данных, и могут, наоборот, создавать дополнительные накладные расходы, если запрос или структура данных им не соответствуют. Если в блоке есть хотя бы одно подходящее значение, весь этот блок все равно придется прочитать. Эффективность использования индексов пропуска данных часто зависит от сильной корреляции между индексируемым столбцом и первичным ключом таблицы либо от вставки данных таким образом, чтобы похожие значения группировались вместе. В целом индексы пропуска данных лучше всего применять после того, как обеспечены корректное проектирование первичного ключа и оптимизация типов. Они особенно полезны для:
  • Столбцов с высокой общей мощностью, но низкой мощностью внутри блока.
  • Редких значений, критически важных для поиска (например, кодов ошибок, конкретных ID).
  • Случаев, когда фильтрация выполняется по столбцам, не входящим в первичный ключ, при локализованном распределении данных.
Всегда:
  1. Тестируйте индексы пропуска данных на реальных данных с реалистичными запросами. Пробуйте разные типы индексов и значения гранулярности.
  2. Оценивайте их влияние с помощью таких инструментов, как send_logs_level=‘trace’ и EXPLAIN indexes=1, чтобы увидеть эффективность индекса.
  3. Всегда оценивайте размер индекса и то, как на него влияет гранулярность. Уменьшение гранулярности часто повышает производительность до определенного предела, поскольку позволяет отфильтровывать больше гранул и уменьшать объем сканирования. Однако по мере роста размера индекса при меньшей гранулярности производительность также может ухудшаться. Измеряйте производительность и размер индекса для разных значений гранулярности. Это особенно актуально для индексов Bloom filter.

При правильном использовании индексы пропуска данных могут дать существенный прирост производительности — при бездумном применении они могут привести к лишним затратам. Более подробное руководство по индексам пропуска данных смотрите здесь.

Пример

Рассмотрим следующую оптимизированную таблицу. Она содержит данные Stack Overflow: по одной строке на каждый пост.
Эта таблица оптимизирована для запросов, которые фильтруют и агрегируют данные по типу поста и дате. Предположим, нам нужно подсчитать количество постов с более чем 10 000 000 просмотров, опубликованных после 2009 года.
Этот запрос может исключить часть строк (и гранул) с помощью основного индекса. Однако большинство строк всё равно необходимо прочитать, что подтверждается приведённым выше результатом и следующим выводом EXPLAIN indexes = 1:
Простой анализ показывает, что ViewCount коррелирует с CreationDate (первичным ключом), как и следовало ожидать: чем дольше существует публикация, тем больше у неё просмотров.
Поэтому здесь логично использовать индекс пропуска данных. Учитывая числовой тип, индекс minmax здесь вполне уместен. Мы добавим индекс с помощью следующих команд ALTER TABLE — сначала добавим его, а затем “материализуем”.
Этот индекс также можно было добавить при создании таблицы. Схема с индексом minmax, заданным в DDL:
Следующая анимация показывает, как для таблицы из примера строится наш minmax-индекс пропуска данных, отслеживающий минимальные и максимальные значения ViewCount для каждого блока строк (гранулы) в таблице: Повторив предыдущий запрос, можно увидеть заметное повышение производительности. Обратите внимание на уменьшение числа просканированных строк:
EXPLAIN indexes = 1 подтверждает, что индекс используется.
Мы также показываем анимацию того, как minmax индекс пропуска данных отбрасывает все блоки строк, которые заведомо не могут содержать совпадений с предикатом ViewCount > 10,000,000 в нашем примере запроса:
Последнее изменение 29 июня 2026 г.