Полнотекстовые инвертированные индексы¶
Начиная с версии 3.3.0, StarRocks поддерживает полнотекстовые инвертированные индексы, которые разбивают текст на слова и создают индексную запись для каждого слова с отображением этого слова на номер строки в файле данных. Для полнотекстовых поисков StarRocks опрашивает инвертированный индекс по ключевым словам и быстро находит строки, соответствующие этим словам.
Таблицы Primary Key поддерживают полнотекстовые инвертированные индексы с v4.0.
С v4.1, помимо реализации по умолчанию на базе CLucene, доступна встроенная (native) реализация инвертированного индекса. Встроенная реализация поддерживает кластеры shared-nothing и shared-data.
Обзор¶
Нижележащие данные StarRocks хранятся в колоночных файлах. Каждый файл может содержать полнотекстовый инвертированный индекс по индексируемым столбцам. Значения индексируемых столбцов токенизируются в отдельные слова. Каждое слово после токенизации становится записью индекса, указывающей на номер строки, где слово встречается. Поддерживаются режимы токенизации: английский, китайский, многоязычный и без токенизации.
Например, если строка данных содержит «hello world», а её номер — 123, инвертированный индекс создаёт записи: hello->123, world->123.
Во время полнотекстового поиска StarRocks находит записи, содержащие ключевые слова, и быстро получает номера строк, где встречаются ключевые слова, что значительно сокращает объём сканируемых данных.
Базовые операции¶
Создание полнотекстового инвертированного индекса¶
Перед созданием включите параметр FE enable_experimental_gin.
ADMIN SET FRONTEND CONFIG ("enable_experimental_gin" = "true");
При создании полнотекстового инвертированного индекса необходимо отключить свойство таблицы replicated_storage.
Для v4.0 и новее оно отключается автоматически при создании индекса.
Для версий ниже v4.0 нужно вручную задать
replicated_storage=false.
Создание при создании таблицы¶
Создание индекса по столбцу v с английской токенизацией:
CREATE TABLE `t` (
`k` BIGINT NOT NULL COMMENT "",
`v` STRING COMMENT "",
INDEX idx (v) USING GIN("parser" = "english")
) ENGINE=OLAP
DUPLICATE KEY(`k`)
DISTRIBUTED BY HASH(`k`) BUCKETS 1
PROPERTIES (
"replicated_storage" = "false"
);
Параметр
parserзадаёт метод токенизации. Значения:none(по умолчанию): без токенизации. Вся строка считается одним элементом индекса.english: английская токенизация (обычно по неалфавитным символам) и приведение к нижнему регистру. Поэтому ключевые слова в запросе должны быть в нижнем регистре.chinese: китайская токенизация с использованием CJK Analyzer в CLucene.standard: многоязычная токенизация (на базе Unicode Text Segmentation). Хорошо работает для большинства языков и смешанных случаев (например, китайский+английский). Английские слова приводятся к нижнему регистру.
Тип данных индексируемого столбца: CHAR, VARCHAR или STRING.
Добавление после создания таблицы¶
ALTER TABLE t ADD INDEX idx (v) USING GIN('parser' = 'english');
CREATE INDEX idx ON t (v) USING GIN('parser' = 'english');
Управление индексом¶
Просмотр индекса¶
MySQL [example_db]> SHOW CREATE TABLE t\G
Удаление индекса¶
DROP INDEX idx on t;
ALTER TABLE t DROP index idx;
Ускорение запросов с помощью инвертированного индекса¶
После создания индекса включите системную переменную enable_gin_filter, чтобы индекс ускорял запросы. Также учитывайте, применяется ли токенизация для индексируемого столбца: от этого зависят поддерживаемые предикаты.
Поддерживаемые запросы при включённой токенизации¶
Если у столбца инвертированного индекса включена токенизация (parser = standard | english | chinese), поддерживаются только предикаты:
<col_name> (NOT) MATCH '%keyword%'<col_name> (NOT) MATCH_ANY 'keyword1, keyword2'<col_name> (NOT) MATCH_ALL 'keyword1, keyword2'
Параметр Keyword должен быть строковым литералом (выражения не поддерживаются). Примеры создания таблицы, вставки данных и запросов оставлены в исходном виде в SQL‑блоках для точности.
Примечания:
Можно выполнять нестрогое совпадение
%keyword%, но keyword должен содержать часть слова. Например,starrocks(с пробелом) не совпадает сstarrocks.При английской/многоязычной токенизации слова в индексе приводятся к нижнему регистру; в запросах используйте нижний регистр.
Предикаты
MATCH/MATCH_ANY/MATCH_ALLдолжны быть pushdown‑предикатами: в WHERE и по индексируемому столбцу.
Поддерживаемые запросы без токенизации¶
Если 'parser' = 'none', для фильтрации по инвертированному индексу поддерживаются pushdown‑предикаты:
Выражения: (NOT) LIKE, (NOT) MATCH, (NOT) MATCH_ANY, (NOT) MATCH_ALL
В этом случае
MATCHсемантически эквивалентенLIKE.MATCHиLIKEподдерживают только формат(NOT) <col_name> MATCH|LIKE '%keyword%'. Keyword — строковый литерал. Если формат иной, даже при корректном выполнении запрос не использует инвертированный индекс.
Регулярные предикаты:
==,!=,<=,>=,NOT IN,IN,IS NOT NULL,NOT NULL.
Проверка ускорения¶
После выполнения запроса проверьте метрики GinFilterRows и GinFilter в узле сканирования профиля запроса.
Встроенные инвертированные индексы¶
С v4.1 StarRocks предоставляет встроенную реализацию инвертированного индекса поверх bitmap‑индекса; поддерживает shared-nothing и shared-data.
CLucene‑реализация не поддерживается в shared-data. Для shared-data используйте встроенную реализацию.
Сравнение реализаций¶
Реализация |
Поддержка с |
Shared-nothing |
Shared-data |
Описание |
|---|---|---|---|---|
CLucene (default) |
v3.3.0 |
Да |
Нет |
Основана на библиотеке CLucene; реализация по умолчанию для shared-nothing. |
built-in |
v4.1.0 |
Да |
Да |
Нативная реализация StarRocks; поддерживает оба режима кластеров. |
Вы можете явно задать тип реализации параметром imp_lib при создании индекса. Если не задан, система выберет автоматически по режиму кластера:
В shared-nothing — по умолчанию CLucene.
В shared-data — по умолчанию built-in (CLucene не поддерживается).
Создание встроенного индекса¶
При создании таблицы¶
-- Create table with built-in inverted index
CREATE TABLE `t_builtin` (
`id1` bigint(20) NOT NULL COMMENT "",
`value` varchar(255) NOT NULL COMMENT "",
INDEX gin_english (`value`) USING GIN ("parser" = "english", "imp_lib" = "builtin") COMMENT 'builtin english index'
)
DUPLICATE KEY(`id1`)
DISTRIBUTED BY HASH(`id1`)
PROPERTIES (
"replicated_storage" = "false"
);
После создания таблицы¶
ALTER TABLE t ADD INDEX idx_builtin (v) USING GIN('parser' = 'english', 'imp_lib' = 'builtin');
-- Or
CREATE INDEX idx_builtin ON t (v) USING GIN('parser' = 'english', 'imp_lib' = 'builtin');
Встроенный индекс в shared-data кластерах¶
В shared-data встроенная реализация выбирается автоматически даже без imp_lib. Можно использовать тот же синтаксис:
CREATE TABLE `t_shared_data` (
`id1` bigint(20) NOT NULL COMMENT "",
`value` varchar(255) NOT NULL COMMENT "",
INDEX gin_english (`value`) USING GIN ("parser" = "english") COMMENT 'english index'
)
DUPLICATE KEY(`id1`)
DISTRIBUTED BY HASH(`id1`);
Либо указать явно:
CREATE TABLE `t_shared_data_explicit` (
`id1` bigint(20) NOT NULL COMMENT "",
`value` varchar(255) NOT NULL COMMENT "",
INDEX gin_english (`value`) USING GIN ("parser" = "english", "imp_lib" = "builtin") COMMENT 'builtin english index'
)
DUPLICATE KEY(`id1`)
DISTRIBUTED BY HASH(`id1`);
dict_gram_num¶
Параметр dict_gram_num доступен только для встроенной реализации. Он управляет размером n‑грамм, используемых для построения n‑gram‑словаря поверх словаря инвертированного индекса, что значительно ускоряет wildcard/substring‑запросы.
Принцип работы¶
При dict_gram_num = 2 строка "starrocks" разбивается на "st", "ta", "ar", "rr", "ro", "oc", "ck", "ks". При запросе MATCH '%rock%' строка "rock" также разбивается на "ro", "oc", "ck", и n‑gram‑индекс быстро сужает кандидатов без полного прохода по словарю.
Детали параметра¶
Параметр |
Значение по умолчанию |
Диапазон |
Описание |
|---|---|---|---|
|
|
Положительные целые |
Размер n‑gram; работает только с |
Рекомендации¶
Меньшее значение (например,
2) генерирует больше n‑грамм на запись (больше индекс), но лучше фильтрует короткие шаблоны.Большее значение (например,
4) уменьшает индекс, но хуже фильтрует короткие шаблоны.Если преобладают короткие wildcard‑паттерны (
%ab%), используйте меньшее значение (например,2).Если длина шаблона короче
dict_gram_num, n‑gram‑индекс не применяется и выполняется полный просмотр словаря.
Примеры¶
Создание со значением dict_gram_num:
CREATE TABLE `t_gram` (
`id1` bigint(20) NOT NULL COMMENT "",
`text_val` varchar(255) NOT NULL COMMENT "",
INDEX idx_gram (`text_val`) USING GIN (
"parser" = "english",
"imp_lib" = "builtin",
"dict_gram_num" = "2"
) COMMENT 'builtin index with ngram'
)
DUPLICATE KEY(`id1`)
DISTRIBUTED BY HASH(`id1`)
PROPERTIES (
"replicated_storage" = "false"
);
Добавление dict_gram_num после создания таблицы:
CREATE INDEX idx_gram ON t (v) USING GIN('parser' = 'english', 'imp_lib' = 'builtin', 'dict_gram_num' = '3');