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

[Предварительная версия] Полнотекстовый инвертированный индекс

Начиная с версии 1.5.0, Selena поддерживает полнотекстовые инвертированные индексы, которые могут разбивать текст на отдельные слова и создавать запись индекса для каждого слова, показывающую отношение между словом и соответствующим номером строки в файле данных. Для полнотекстового поиска Selena запрашивает инвертированный индекс на основе ключевых слов поиска, быстро находя строки данных, соответствующие ключевым словам.

Полнотекстовый инвертированный индекс пока не поддерживается в таблицах Primary Key и кластерах с общими данными.

Обзор

Selena хранит свои базовые данные в файлах данных, организованных по столбцам. Каждый файл данных содержит полнотекстовый инвертированный индекс на основе индексированных столбцов. Значения в индексированных столбцах токенизируются на отдельные слова. Каждое слово после токенизации рассматривается как запись индекса, сопоставляемая с номером строки, где появляется слово. В настоящее время поддерживаются методы токенизации для английской токенизации, китайской токенизации, многоязычной токенизации и без токенизации.

Например, если строка данных содержит "hello world" и её номер строки 123, полнотекстовый инвертированный индекс создает записи индекса на основе этого результата токенизации и номера строки: hello->123, world->123.

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

Основные операции

Создание полнотекстового инвертированного индекса

Перед созданием полнотекстового инвертированного индекса необходимо включить параметр конфигурации FE enable_experimental_gin.

ADMIN SET FRONTEND CONFIG ("enable_experimental_gin" = "true");

Также полнотекстовый инвертированный индекс может быть создан только в таблице Duplicate Key, и свойство таблицы 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) и хорошо работает для большинства языков и случаев смешанных языков, таких как китайский и английский. Например, этот метод токенизации может различать китайский и английский, когда эти два языка сосуществуют. После токенизации английского он преобразует заглавные английские буквы в строчные. Поэтому ключевые слова в условиях запроса должны быть строчными английскими, а не заглавными английскими, чтобы использовать полнотекстовый инвертированный индекс для поиска строк данных.
  • Тип данных индексированного столбца должен быть CHAR, VARCHAR или STRING.

Добавление полнотекстового инвертированного индекса после создания таблицы

После создания таблицы можно добавить полнотекстовый инвертированный индекс, используя ALTER TABLE ADD INDEX или CREATE INDEX.

ALTER TABLE t ADD INDEX idx (v) USING GIN('parser' = 'english');
CREATE INDEX idx ON t (v) USING GIN('parser' = 'english');

Управление полнотекстовым инвертированным индексом

Просмотр полнотекстового инвертированного индекса

Выполните SHOW CREATE TABLE для просмотра полнотекстовых инвертированных индексов.

MySQL [example_db]> SHOW CREATE TABLE t\G

Удаление полнотекстового инвертированного индекса

Выполните ALTER TABLE ADD INDEX или DROP INDEX для удаления полнотекстовых инвертированных индексов.

DROP INDEX idx on t;
ALTER TABLE t DROP index idx;

Ускорение запросов с помощью полнотекстового инвертированного индекса

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

Поддерживаемые запросы при токенизации индексированного столбца

Если полнотекстовый инвертированный индекс токенизирует индексированные столбцы, то есть 'parser' = 'standard|english|chinese', для фильтрации данных с использованием полнотекстовых инвертированных индексов поддерживается только предикат MATCH, и формат должен быть <col_name> (NOT) MATCH '%keyword%'. keyword должно быть строковым литералом и не поддерживает выражения.

  1. Создайте таблицу и вставьте несколько строк тестовых данных.

    CREATE TABLE `t` (
    `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`)
    PROPERTIES (
    "replicated_storage" = "false"
    );


    INSERT INTO t VALUES
    (1, "starrocks is a database

    1"),
    (2, "starrocks is a data warehouse");
  2. Используйте предикат MATCH для запросов.

  • Запрос строк данных, столбец value которых содержит ключевое слово starrocks.

    MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "starrocks";
  • Получение строк данных, столбец value которых содержит ключевое слово, начинающееся с data.

    MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "data%";

Примечания:

  • Во время запросов ключевые слова могут быть сопоставлены нечетко с использованием % в формате %keyword%. Однако ключевое слово должно содержать часть слова. Например, если ключевое слово starrocks , оно не может соответствовать слову starrocks, поскольку содержит пробелы.

    MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "star%";
    +------+-------------------------------+
    | id1 | value |
    +------+-------------------------------+
    | 1 | starrocks is a database1 |
    | 2 | starrocks is a data warehouse |
    +------+-------------------------------+
    2 rows in set (0.02 sec)

    MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "starrocks ";
    Empty set (0.02 sec)
  • Если для построения полнотекстового инвертированного индекса используется английская или многоязычная токенизация, заглавные английские слова преобразуются в строчные при фактическом хранении полнотекстового инвертированного индекса. Поэтому во время запросов ключевые слова должны быть строчными, а не заглавными, чтобы использовать полнотекстовый инвертированный индекс для поиска строк данных.

    MySQL [example_db]> INSERT INTO t VALUES (3, "StarRocks is the BEST");

    MySQL [example_db]> SELECT * FROM t;
    +------+-------------------------------+
    | id1 | value |
    +------+-------------------------------+
    | 1 | starrocks is a database |
    | 2 | starrocks is a data warehouse |
    | 3 | StarRocks is the BEST |
    +------+-------------------------------+
    3 rows in set (0.02 sec)

    MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "BEST"; -- Ключевое слово заглавными английскими буквами
    Empty set (0.02 sec) -- Возвращает пустой набор результатов

    MySQL [example_db]> SELECT * FROM t WHERE t.value MATCH "best"; -- Ключевое слово строчными английскими буквами
    +------+-----------------------+
    | id1 | value |
    +------+-----------------------+
    | 3 | StarRocks is the BEST | -- Может найти строки данных, соответствующие условию
    +------+-----------------------+
    1 row in set (0.01 sec)
  • Предикат MATCH в условиях запроса должен использоваться как предикат pushdown, поэтому он должен быть в предложении WHERE и выполняться против индексированного столбца.

    Возьмем следующую таблицу и тестовые данные в качестве примера:

    CREATE TABLE `t_match` (
    `id1` bigint(20) NOT NULL COMMENT "",
    `value` varchar(255) NOT NULL COMMENT "",
    `value_test` varchar(255) NOT NULL COMMENT "",
    INDEX gin_english (`value`) USING GIN("parser" = "english") COMMENT 'english index'
    )
    ENGINE=OLAP
    DUPLICATE KEY(`id1`)
    DISTRIBUTED BY HASH (`id1`) BUCKETS 1
    PROPERTIES (
    "replicated_storage" = "false"
    );

    INSERT INTO t_match VALUES (1, "test", "test");

    Следующие операторы запроса не соответствуют требованию:

    • Поскольку предикат MATCH в операторе запроса не находится в предложении WHERE, он не может быть передан вниз, что приводит к ошибке запроса.

      MySQL [test]> SELECT value MATCH "test" FROM t_match;
      ERROR 1064 (HY000): Match can only be used as a pushdown predicate on a column with GIN in a single query.
    • Поскольку столбец value_test, против которого выполняется предикат MATCH в операторе запроса, не является индексированным столбцом, запрос завершается неудачей.

      MySQL [test]> SELECT * FROM t_match WHERE value_test match "test";
      ERROR 1064 (HY000): Match can only be used as a pushdown predicate on a column with GIN in a single query.

Поддерживаемые запросы при отсутствии токенизации индексированного столбца

Если полнотекстовый инвертированный индекс не токенизирует индексированный столбец, то есть 'parser' = 'none', все предикаты pushdown в условиях запроса, перечисленные ниже, могут использоваться для фильтрации данных с использованием полнотекстового инвертированного индекса:

  • Предикаты выражений: (NOT) LIKE, (NOT) MATCH

    примечание
    • В этом случае MATCH семантически эквивалентен LIKE.
    • MATCH и LIKE поддерживают только формат (NOT) <col_name> MATCH|LIKE '%keyword%'. keyword должно быть строковым литералом и не поддерживает выражения. Обратите внимание, что если LIKE не соответствует этому формату, даже если запрос может выполняться нормально, он будет деградировать до запроса, который не использует полнотекстовый инвертированный индекс для фильтрации данных.
  • Обычные предикаты: ==, !=, <=, >=, NOT IN, IN, IS NOT NULL, NOT NULL

Как проверить, ускоряет ли полнотекстовый инвертированный индекс запросы

После выполнения запроса можно просмотреть подробные метрики GinFilterRows и GinFilter в узле сканирования Query Profile, чтобы увидеть количество отфильтрованных строк и время фильтрации с использованием полнотекстового инвертированного индекса.