Learning Platform
Глоссарий Troubleshooting
Урок 06.02 · 20 мин
Средний
Text IndexInverted IndexFull-text SearchhasTokenngrambf_v1tokenbf_v1

Текстовые индексы (inverted)

В предыдущем уроке мы видели, что ngrambf_v1 и tokenbf_v1 используют Bloom-фильтры для текстового поиска — и дают ложные срабатывания. ClickHouse предлагает современную замену: text index (inverted index). Это GA-фича, production-ready, которая обеспечивает детерминированный полнотекстовый поиск без ложных срабатываний.


Чем text index отличается от Bloom-фильтров

Свойствоngrambf_v1 / tokenbf_v1text index
СтатусУстаревшие (legacy)GA (production-ready)
Внутренняя структураBloom-фильтрInverted index (token -> номера строк)
Ложные срабатыванияДа (вероятностный)Нет (детерминированный)
Производительность (холодные запросы)Базовая7-10x быстрее
Поддерживаемые типыStringString, Array(String), Map ключи и значения

Ключевое отличие: text index строит настоящий инвертированный индекс — маппинг “токен -> номера строк, содержащих этот токен”. Когда запрос ищет токен “error”, индекс точно знает, в каких строках он встречается. Bloom-фильтр же может только ответить “возможно есть” (с вероятностью ложного срабатывания).


Создание text index

CREATE TABLE logs (
    timestamp DateTime,
    service String,
    message String,
    INDEX idx_message message TYPE text GRANULARITY 1
) ENGINE = MergeTree()
ORDER BY (service, timestamp);

TYPE text — тип индекса. GRANULARITY 1 — одна запись индекса на каждую гранулу (максимальная точность).

Добавление к существующей таблице:

ALTER TABLE logs ADD INDEX idx_message message TYPE text GRANULARITY 1;
ALTER TABLE logs MATERIALIZE INDEX idx_message;

Запросы с text index

Text index поддерживает несколько функций поиска:

hasToken — точное совпадение токена

-- Ищет строки, содержащие токен "error" (целое слово)
SELECT timestamp, service, message
FROM logs
WHERE hasToken(message, 'error');

hasToken разбивает строку на токены по не-алфавитно-цифровым разделителям. Строка “connection error: timeout” содержит токены: “connection”, “error”, “timeout”. Запрос hasToken(message, 'error') найдёт эту строку.

hasAny / hasAll — множественный поиск токенов

-- Строки, содержащие хотя бы один из токенов
SELECT * FROM logs
WHERE hasToken(message, 'error') OR hasToken(message, 'fatal');

-- Строки, содержащие все указанные токены
SELECT * FROM logs
WHERE hasToken(message, 'connection') AND hasToken(message, 'timeout');

Подстрочный поиск (LIKE)

-- Text index ускоряет и подстрочный поиск
SELECT * FROM logs WHERE message LIKE '%timeout%';

Поддержка сложных типов

Text index работает не только с обычными строками:

Array(String)

CREATE TABLE events (
    timestamp DateTime,
    tags Array(String),
    INDEX idx_tags tags TYPE text GRANULARITY 1
) ENGINE = MergeTree()
ORDER BY timestamp;

-- Поиск по массиву тегов
SELECT * FROM events WHERE has(tags, 'production');

Map ключи и значения

CREATE TABLE traces (
    timestamp DateTime,
    attributes Map(String, String),
    INDEX idx_attrs_keys mapKeys(attributes) TYPE text GRANULARITY 1,
    INDEX idx_attrs_values mapValues(attributes) TYPE text GRANULARITY 1
) ENGINE = MergeTree()
ORDER BY timestamp;

-- Поиск по ключам и значениям Map
SELECT * FROM traces
WHERE attributes['service.name'] = 'payment-api';

Как text index работает внутри

Text index: от запроса до данных
Запрос: hasToken(message, 'error')Запрос с hasToken(message, 'error') -- ClickHouse извлекает токен 'error' из условия фильтрации и обращается к inverted index.
lookup
Text index: error -> [строки 42, 108, 9451]Inverted index -- маппинг token -> posting list (номера строк). Для токена 'error' возвращает список строк, содержащих это слово. Детерминированный результат -- ложных срабатываний нет.
row IDs
Чтение только нужных гранулЧтение данных -- ClickHouse читает только гранулы, содержащие найденные строки. Остальные гранулы полностью пропускаются. На холодных запросах это даёт ускорение в 7-10 раз.

Миграция с ngrambf_v1 / tokenbf_v1 на text index

Если у вас есть существующие таблицы с устаревшими Bloom-индексами:

-- 1. Удалить старый индекс
ALTER TABLE logs DROP INDEX idx_message_ngram;

-- 2. Добавить text index
ALTER TABLE logs ADD INDEX idx_message message TYPE text GRANULARITY 1;

-- 3. Материализовать для существующих данных
ALTER TABLE logs MATERIALIZE INDEX idx_message;
TIP

Миграция не требует пересоздания таблицы. Просто замените старый индекс новым. Новый text index будет работать для всех существующих данных после MATERIALIZE INDEX.


Ключевые выводы

  1. Text index — GA-фича ClickHouse, заменяющая ngrambf_v1 и tokenbf_v1 для полнотекстового поиска.
  2. В отличие от Bloom-фильтров, text index создаёт детерминированный inverted index (token -> номера строк) — ложных срабатываний нет.
  3. Производительность на холодных запросах в 7-10 раз выше, чем у Bloom-индексов.
  4. Поддерживает String, Array(String), Map ключи и значения.
  5. Основные функции: hasToken() для токенного поиска, LIKE для подстрочного.
Full-text search в PostgreSQL: tsvector, tsquery и ранжирование B-tree: физическая структура индекса

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 3. Таблица логов содержит миллиарды строк. Нужен быстрый поиск по hasToken(message, 'error'). Какой индекс является современной GA-заменой ngrambf_v1 для этой задачи?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7