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

Примеры индексов пропуска данных

На этой странице собраны примеры индексов пропуска данных ClickHouse, показано, как объявить каждый тип, когда их использовать и как проверить, что они используются. Все возможности работают с таблицами семейства MergeTree.

Синтаксис индекса:

INDEX имя выражение TYPE тип(...) [GRANULARITY N]

ClickHouse поддерживает пять типов индексов пропуска данных:

Index TypeDescription
minmaxОтслеживает минимальные и максимальные значения в каждой грануле
set(N)Хранит до N различных значений в каждой грануле
bloom_filter([false_positive_rate])Вероятностный фильтр для проверки наличия значений
ngrambf_v1N-граммовый bloom-фильтр для поиска подстрок
tokenbf_v1Bloom-фильтр на основе токенов для полнотекстового поиска

В каждом разделе приводятся примеры с демонстрационными данными и показывается, как проверить использование индекса при выполнении запроса.

Индекс MinMax

Индекс minmax лучше всего подходит для диапазонных предикатов по слабо упорядоченным данным или по столбцам, коррелированным с ORDER BY.

-- Определение при создании таблицы (CREATE TABLE)
CREATE TABLE events
(
  ts DateTime,
  user_id UInt64,
  value UInt32,
  INDEX ts_minmax ts TYPE minmax GRANULARITY 1
)
ENGINE=MergeTree
ORDER BY ts;

-- Или добавить позже и материализовать
ALTER TABLE events ADD INDEX ts_minmax ts TYPE minmax GRANULARITY 1;
ALTER TABLE events MATERIALIZE INDEX ts_minmax;

-- Запрос, который использует индекс
SELECT count() FROM events WHERE ts >= now() - 3600;

-- Проверка использования
EXPLAIN indexes = 1
SELECT count() FROM events WHERE ts >= now() - 3600;

См. подробный пример с EXPLAIN и отсечением данных.

Индекс set

Используйте индекс set, когда локальная кардинальность на уровне блока низкая; он неэффективен, если в каждом блоке много различных значений.

ALTER TABLE events ADD INDEX user_set user_id TYPE set(100) GRANULARITY 1;
ALTER TABLE events MATERIALIZE INDEX user_set;

SELECT * FROM events WHERE user_id IN (101, 202);

EXPLAIN indexes = 1
SELECT * FROM events WHERE user_id IN (101, 202);

Процесс создания и материализации, а также эффект до/после показаны в базовом руководстве по работе.

Универсальный Bloom-фильтр (скалярный)

Индекс bloom_filter хорошо подходит для поиска «иголки в стоге сена» по условию равенства или проверки принадлежности (оператор IN). Он принимает необязательный параметр — вероятность ложноположительных срабатываний (по умолчанию 0.025).

ALTER TABLE events ADD INDEX value_bf value TYPE bloom_filter(0.01) GRANULARITY 3;
ALTER TABLE events MATERIALIZE INDEX value_bf;

SELECT * FROM events WHERE value IN (7, 42, 99);

EXPLAIN indexes = 1
SELECT * FROM events WHERE value IN (7, 42, 99);

N-граммный фильтр Блума (ngrambf_v1) для поиска подстрок

Индекс ngrambf_v1 разбивает строки на n-граммы. Он хорошо подходит для запросов вида LIKE '%...%'. Поддерживаются типы String/FixedString/Map (через mapKeys/mapValues), а также настраиваемые размер, количество хэшей и значение seed. Дополнительные сведения см. в документации по N-граммному фильтру Блума.

-- Создать индекс для поиска подстроки
ALTER TABLE logs ADD INDEX msg_ngram msg TYPE ngrambf_v1(3, 10000, 3, 7) GRANULARITY 1;
ALTER TABLE logs MATERIALIZE INDEX msg_ngram;

-- Поиск подстроки
SELECT count() FROM logs WHERE msg LIKE '%timeout%';

EXPLAIN indexes = 1
SELECT count() FROM logs WHERE msg LIKE '%timeout%';

В этом руководстве приведены практические примеры и указано, в каких случаях использовать token, а в каких — ngram.

Вспомогательные инструменты для оптимизации параметров:

Четыре параметра ngrambf_v1 (размер n-граммы, размер битовой карты, количество хеш-функций, seed) существенно влияют на производительность и потребление памяти. Используйте эти функции, чтобы вычислить оптимальный размер битовой карты и количество хеш-функций исходя из ожидаемого объёма n-грамм и требуемой доли ложноположительных срабатываний:

CREATE FUNCTION bfEstimateFunctions AS
(total_grams, bits) -> round((bits / total_grams) * log(2));

CREATE FUNCTION bfEstimateBmSize AS
(total_grams, p_false) -> ceil((total_grams * log(p_false)) / log(1 / pow(2, log(2))));

-- Пример расчёта размера для 4300 n-грамм, p_false = 0.0001
SELECT bfEstimateBmSize(4300, 0.0001) / 8 AS size_bytes;  -- ~10304
SELECT bfEstimateFunctions(4300, bfEstimateBmSize(4300, 0.0001)) AS k; -- ~13

См. документацию по параметрам для получения подробных рекомендаций по настройке.

Токен-блум-фильтр (tokenbf_v1) для поиска по словам

tokenbf_v1 индексирует токены, разделённые небуквенно-цифровыми символами. Используйте его с hasToken, с шаблонами слов LIKE или с операторами равенства (=) и IN. Поддерживает типы String/FixedString/Map.

См. разделы Token bloom filter и Bloom filter types для получения дополнительной информации.

ALTER TABLE logs ADD INDEX msg_token lower(msg) TYPE tokenbf_v1(10000, 7, 7) GRANULARITY 1;
ALTER TABLE logs MATERIALIZE INDEX msg_token;

-- Поиск слова (без учёта регистра через lower)
SELECT count() FROM logs WHERE hasToken(lower(msg), 'exception');

EXPLAIN indexes = 1
SELECT count() FROM logs WHERE hasToken(lower(msg), 'exception');

См. примеры по наблюдаемости и рекомендации по выбору token vs ngram здесь.

Добавление индексов при выполнении CREATE TABLE (несколько примеров)

Индексы пропуска данных также поддерживают составные выражения и типы Map/Tuple/Nested. Это показано в примере ниже:

CREATE TABLE t
(
  u64 UInt64,
  s String,
  m Map(String, String),

  INDEX idx_bf u64 TYPE bloom_filter(0.01) GRANULARITY 3,
  INDEX idx_minmax u64 TYPE minmax GRANULARITY 1,
  INDEX idx_set u64 * length(s) TYPE set(1000) GRANULARITY 4,
  INDEX idx_ngram s TYPE ngrambf_v1(3, 10000, 3, 7) GRANULARITY 1,
  INDEX idx_token mapKeys(m) TYPE tokenbf_v1(10000, 7, 7) GRANULARITY 1
)
ENGINE = MergeTree
ORDER BY u64;

Материализация индекса на существующих данных и проверка

Вы можете добавить индекс к уже существующим частям данных с помощью MATERIALIZE и проверить отсечение с помощью EXPLAIN или трассировочных журналов, как показано ниже:

ALTER TABLE t MATERIALIZE INDEX idx_bf;

EXPLAIN indexes = 1
SELECT count() FROM t WHERE u64 IN (123, 456);

-- Опционально: подробная информация о прунинге
SET send_logs_level = 'trace';

Этот подробный пример с minmax демонстрирует структуру вывода EXPLAIN и счётчики отсечения.

Когда использовать и когда избегать индексов пропуска данных

Используйте индексы пропуска данных, когда:

  • Значения фильтра редко встречаются внутри блоков данных
  • Существует сильная корреляция со столбцами ORDER BY или шаблоны ингестии данных группируют похожие значения вместе
  • Выполняется текстовый поиск по большим наборам логов (типы ngrambf_v1/tokenbf_v1)

Избегайте индексов пропуска данных, когда:

  • Большинство блоков, скорее всего, содержит хотя бы одно совпадающее значение (блоки всё равно будут прочитаны)
  • Фильтрация выполняется по высококардинальным столбцам без корреляции с порядком хранения данных
Важные соображения

Если значение встречается хотя бы один раз в блоке данных, ClickHouse должен прочитать весь блок. Тестируйте индексы на реалистичных наборах данных и корректируйте гранулярность и зависящие от типа параметры на основе фактических показателей производительности.

Временно игнорировать или принудительно использовать индексы

Отключайте отдельные индексы по имени для конкретных запросов во время тестирования и устранения неполадок. Также доступны настройки для принудительного использования индексов при необходимости. См. ignore_data_skipping_indices.

-- Игнорирование индекса по имени
SELECT * FROM logs
WHERE hasToken(lower(msg), 'exception')
SETTINGS ignore_data_skipping_indices = 'msg_token';

Примечания и ограничения

  • Индексы пропуска данных поддерживаются только для таблиц семейства MergeTree; отсечение данных выполняется на уровне гранулы/блока.
  • Индексы на основе Bloom-фильтра являются вероятностными (ложные срабатывания приводят к дополнительным чтениям, но не приводят к пропуску корректных данных).
  • Bloom-фильтры и другие индексы пропуска данных следует проверять с помощью EXPLAIN и трассировки; подбирайте гранулярность, чтобы сбалансировать степень отсечения данных и размер индекса.