На этой странице собраны примеры индексов пропуска данных в ClickHouse: показано, как объявлять каждый тип, когда их использовать и как проверять, что они действительно применяются. Все эти возможности поддерживаются таблицами семейства MergeTree.
Синтаксис индекса:
INDEX name expr TYPE type(...) [GRANULARITY N]ClickHouse поддерживает шесть типов индекса пропуска данных:
| Тип индекса | Описание |
|---|---|
| minmax | Отслеживает минимальное и максимальное значение в каждой грануле |
| set(N) | Хранит до N различных значений в каждой грануле |
| text | Инвертированный индекс по токенизированным строковым данным для полнотекстового поиска |
| bloom_filter([false_positive_rate]) | Вероятностный фильтр для проверки существования |
| ngrambf_v1 | N-граммный bloom-фильтр для поиска по подстрокам |
| tokenbf_v1 | Bloom-фильтр на основе токенов для полнотекстового поиска |
В каждом разделе приведены примеры с тестовыми данными и показано, как проверить использование индекса при выполнении запроса.
Индекс 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);Процесс создания и материализации, а также результат до и после, показаны в руководстве по основным операциям.
Текстовый индекс (text) для полнотекстового поиска
text — это инвертированный индекс для токенизированных текстовых данных.
Он специально разработан для задач полнотекстового поиска и обеспечивает эффективный и детерминированный поиск токенов и терминов.
Рекомендуется для сценариев, связанных с обработкой естественного языка или крупномасштабным поиском по тексту.
Подробнее и примеры см. в разделе Полнотекстовый поиск с текстовыми индексами.
ALTER TABLE logs ADD INDEX msg_text msg TYPE text(tokenizer = splitByNonAlpha);
ALTER TABLE logs MATERIALIZE INDEX msg_text;
SELECT count() FROM logs WHERE hasAllTokens(msg, 'exception');Более подробный пример обсервабилити см. в документации здесь.
Текстовый индекс полностью детерминирован и гибко настраивается с точки зрения токенизации и обработки текста, но требует несколько больше места в хранилище по сравнению с индексами на основе фильтра Блума,
Универсальный 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-граммный bloom-фильтр (ngrambf_v1) для поиска по подстрокам (Устарело)
Индекс ngrambf_v1 разбивает строки на n-граммы. Он хорошо подходит для запросов LIKE '%...%'. Поддерживаются String/FixedString/Map (через mapKeys/mapValues), а также настраиваемые размер, количество хеш-функций и seed. Подробнее см. в документации по N-граммному bloom-фильтру.
-- Создание индекса для поиска подстрок
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См. документацию по параметрам для получения подробных рекомендаций по настройке.
Token bloom-фильтр (tokenbf_v1) для поиска по словам (Устарело)
Индексы tokenbf_v1 индексируют токены, разделённые небуквенно-цифровыми символами. Их следует использовать с hasToken, шаблонами слов для LIKE или операторами =/IN. Поддерживаются типы String/FixedString/Map.
Подробнее см. на страницах Token bloom-фильтр и Типы bloom-фильтра.
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');См. примеры по обсервабилити и рекомендации по выбору между токенами и n-граммами здесь.
Добавление индексов при 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)
Избегайте индексов пропуска данных, когда:
- Большинство блоков, скорее всего, содержит хотя бы одно подходящее значение (поэтому блоки всё равно будут прочитаны)
- Фильтрация выполняется по столбцам с высокой мощностью без корреляции с порядком данных
Временно игнорировать индексы или принудительно задействовать их
Отключайте определённые индексы по имени для отдельных запросов во время тестирования и диагностики. При необходимости также доступны настройки, позволяющие принудительно использовать индексы. См. ignore_data_skipping_indices.
-- Ignore an index by name
SELECT * FROM logs
WHERE hasToken(lower(msg), 'exception')
SETTINGS ignore_data_skipping_indices = 'msg_token';Примечания и ограничения
- Индекс пропуска данных поддерживается только для таблиц семейства MergeTree; отсечение данных происходит на уровне гранулы/блока.
- Индексы на основе bloom-фильтров являются вероятностными (ложноположительные срабатывания приводят к дополнительным чтениям, но не к пропуску корректных данных).
- Bloom-фильтры и другие индексы пропуска данных следует проверять с помощью
EXPLAINи трассировки; настраивайте гранулярность, чтобы сбалансировать степень отсечения и размер индекса.