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

rum. Использование RUM индексов

В исходном дистрибутиве установлено по умолчанию: нет.

Связанные компоненты: отсутствуют.

Схема размещения: ext.

Индексный метод доступа RUM является одним из инструментов оптимизации доступа к данным. RUM основан на GIN-подобном индексном методе и включает дополнительную методику оценки релевантности результата поиска («дистанцией»).

Функциональность поставляется в качестве расширения в составе продукта, не требует специальных шагов по настройке со стороны сотрудников сопровождения. Активация расширения не требует изменения параметров конфигурации БД.

RUM имеет качественное преимущество в возможности ранжирования результатов поиска по «дистанции»/«схожести». При этом «дистанция»/«схожесть» имеют другой алгоритм обработки, чем существующие функции ts_rank, ts_rank_cd.

Для незначительного уменьшения размера индекса можно использовать классы операторов hash, если не предполагается префиксного поиска.

RUM имеет количественное преимущество перед существующими индексными методами только при соблюдении следующих условий:

  1. Запрос SQL должен иметь условие поиска по дистанции. В случае FTS - tsquery должен использовать оператор дистанции «<=>»,«<2>»,«<3>» и тому подобные.
  2. Запрос SQL, содержащий условие поиска по дистанции, должен оперировать термами низкой или средней селективности (при возможности их оценки).

В иных случаях предпочтительнее использовать другие индексы.

Расширение предоставляет индексный метод RUM, реализующий следующие классы операторов:

Класс операторовОператорТип операнда 1Тип операнда 2Комментарий
Операторы вне классов операторов<=>tsvectortsqueryДистанция между операндами
timestamptimestamp-
int2int2-
int4int4-
int8int8-
float4float4-
float8float8-
moneymoney-
oidoid-
anyarrayanyarray-
=>timestamptimestampДистанция только для операнда 2
timestamptztimestamptz-
int2int2-
int4int4-
int8int8-
float4float4-
float8float8-
moneymoney-
oidoid-
<=timestamptimestampДистанция только для операнда 1
timestamptztimestamptz-
int2int2-
int4int4-
int8int8-
float4float4-
float8float8-
moneymoney-
oidoid-
% anyarrayanyarrayСходство массивов. Под сходством определяется:
наличие общих элементов массивов
размерность массивов соразмерна
rum_tsvector_ops@@tsvectortsqueryПересечение операндов
rum_tsquery_ops@@tsquerytsvectorПересечение операндов
<=>Дистанция между операндарми
rum_tsvector_hash_ops@@tsvectortsqueryПересечение операндов.
Класс операторов не поддерживает префикс в FTS, но занимает ~ на 5% меньше места
<=>Дистанция между операндами
rum_tsvector_addon_ops@@tsvectortsqueryПересечение операндов
rum_tsvector_hash_addon_ops@@tsvectortsqueryПересечение операндов.
Класс операторов не поддерживает префикс в FTS, но занимает ~ на 5% меньше места
rum_timestamp_ops<timestamp timestamp-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_timestamptz_opstimestamptztimestamptz-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_int2_ops<int2int2-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_int4_ops<int4int4-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_int8_ops<int8int8-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>-
<=-
rum_float4_ops<float4float4-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_float8_ops<float8float8-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_money_ops<moneymoney-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_oid_ops<oidoid-
<=-
=-
>=-
>-
<=>Дистанция между операндами
=>Дистанция только для операнда 2
<=Дистанция только для операнда 1
rum_time_ops<timetime-
<=-
=-
>=-
>-
rum_timetz_ops<timetztimetz-
<=-
=-
>=-
=-
rum_date_ops<datedate-
<=-
=-
>=-
>-
rum_interval_ops<intervalinterval-
<=-
=-
>=-
>-
rum_macaddr_ops<macaddrmacaddr-
<=-
=-
>=-
>-
rum_inet_ops<inetinet-
<=-
=-
>=-
>-
rum_cidr_ops<cidrcidr-
<=-
=-
>=-
>-
rum_text_ops<texttext-
<=-
=-
>=-
>-
rum_varchar_ops<varcharvarchar-
<=-
=-
>=-
>-
rum_char_ops<charchar-
<=-
=-
>=-
>-
rum_bytea_ops<byteabytea-
<=-
=-
>=-
>-
rum_bit_ops<bitbit-
<=-
=-
>=-
>-
rum_varbit_ops<varbitvarbit-
<=-
=-
>=-
>-
rum_numeric_ops<numericnumeric-
<=-
=-
>=-
>-
rum_anyarray_ops&&anyarrayanyarray-
@>-
<@-
=-
%-
<=>Дистанция между операндами
rum_anyarray_addon_ops&&anyarrayanyarrayКласс операторов не поддерживает определение сходства и дистанции
@>-
<@-
=-
rum_tsvector_timestamp_ops@@tsvectortsqueryКласс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamp
rum_tsvector_hash_timestamp_ops@@tsvectortsqueryКласс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamp.Не поддерживает префикс в FTS, но занимает ~ на 5% меньше места
rum_tsvector_timestamptz_ops@@tsvectortsqueryКласс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamptz
rum_tsvector_hash_timestamptz_ops@@tsvectortsqueryКласс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamp.Не поддерживает префикс в FTS, но занимает ~ на 5% меньше места

Примечание:

В столбце Класс операторов класс по умолчанию для сочетания операндов.

Операторы @@, <=>, &&, <, <=, =>, >, < являются коммутативными.

Функции

Расширение добавляет следующие публичные функции (за исключением функций, обеспечивающих работу операторов или классов операторов):

ФункцияОписание
rum_ts_distance (tsvector,(tsquery[,int]\rum_distance_query)::float4Расчет дистанции между tsvector и tsquery с использованием определенного метода подсчета, либо расчет дистанции между tsvector и специальным типом rum_distance_query.
rum_ts_score(tsvector,(tsquery[,int]\rum_distance_query)::float4Расчет рейтинга между tsvector и tsquery с использованием определенного метода подсчета, либо расчет дистанции между tsvector и специальным типом rum_distance_query.
Рейтинг обратно пропорционален дистанции

Метод подсчета – битовая маска, учитывающая нормализацию распределения веса к объему документа и соответствующая таковой в функции ts_rank_cd индекса GIN.

Метод подсчетаЗначение маскиОписание
RANK_NO_NORM0x00Длина документа не учитывается
RANK_NORM_LOGLENGTH0x01Ранг документа делится на сумму единицы и логарифма длины документа
RANK_NORM_LENGTH0x02Ранг документа делится на его длину
RANK_NORM_EXTDIST0x04Ранг документа делится на среднее гармоническое расстояние между блоками
RANK_NORM_UNIQ0x08Ранг документа делится на число уникальных слов в документе
RANK_NORM_LOGUNIQ0x10Ранг документа делится на сумму единицы и логарифма числа уникальных слов в документе
RANK_NORM_RDIVRPLUS10x20Ранг делится на значение, которое на единицу больше самого ранга

Доработка

Доработка не проводилась.

Ограничения

Ограничения отсутствуют.

Установка

Установка расширения производится в выбранной БД следующим способом:

SET ROLE db_admin;
CREATE EXTENSION IF NOT EXISTS rum WITH SCHEMA ext;
RESET ROLE;

Отключение расширения

Отключение расширения влечет за собой удаление всех ранее созданных индексов RUM, дальнейшую невозможность использовать классы операторов, операторы и функции, добавленные расширением. Отключение функциональности требует прав db_admin.

Удаление расширения производится следующим способом:

SET ROLE db_admin;
DROP EXTENSION IF EXISTS rum CASCADE;
RESET ROLE;

Настройка

Настройка не требуется.

Использование модуля

Подготовительные действия

  1. Создайте расширение rum:

    SET ROLE db_admin;
    CREATE EXTENSION IF NOT EXISTS rum WITH SCHEMA ext;
    CREATE SCHEMA IF NOT EXISTS test AUTHORIZATION as_admin;
    RESET ROLE;
    SET ROLE as_admin;
  2. Создайте конфигурации FTS и словаря:

    CREATE TEXT SEARCH DICTIONARY ext.english_hunspell (
    TEMPLATE = pg_catalog.ispell,
    dictfile = 'en', afffile = 'en', stopwords = 'english' );

    CREATE TEXT SEARCH CONFIGURATION ext.english_dict (COPY=pg_catalog.english);
    ALTER TEXT SEARCH CONFIGURATION ext.english_dict ALTER MAPPING FOR
    asciihword
    ,asciiword
    ,hword
    ,hword_asciipart
    ,hword_part
    ,word
    WITH ext.english_hunspell, english_stem;

  3. Скопируйте конфигурации со стеммером:

    CREATE TEXT SEARCH CONFIGURATION ext.english (COPY=pg_catalog.english);
  4. Создайте таблицу для тестов (вариант RUM):

    CREATE TABLE test.tbl_rum (LIKE test.tbl_plain INCLUDING ALL);
  5. Создайте FTS индексы:

    CREATE INDEX tbl_gin_to_tsvector_idx ON test.tbl_gin USING gin (to_tsvector('ext.english_dict'::regconfig, val));
    CREATE INDEX tbl_gin_to_tsvector_idx1 ON test.tbl_gin USING gin (to_tsvector('ext.english'::regconfig, val));

    CREATE INDEX tbl_rum_to_tsvector_idx ON test.tbl_rum USING rum (to_tsvector('ext.english_dict'::regconfig, val));
    CREATE INDEX tbl_rum_to_tsvector_idx1 ON test.tbl_rum USING rum (to_tsvector('ext.english'::regconfig, val));
    RESET ROLE;

Сценарии использования

Использование индекса в сценарии FTS, использующего операторы дистанции, FTS словарь

  1. Запустите блок кода (использование FTS без индексов):

    EXPLAIN (ANALYZE,BUFFERS) SELECT
    id
    ,pattern
    ,tsv_txt
    ,tsq
    ,val
    FROM
    (SELECT
    ts_headline('ext.english_dict',val,tsq,'HighlightAll=true,StartSel=<<*,StopSel=*>>') pattern
    ,id
    ,regexp_split_to_array(tsv::text,' ') tsv_txt
    ,tsv
    ,tsq
    ,val
    FROM (SELECT
    id
    ,to_tsquery('ext.english_dict','database <2> server & !sql') tsq
    ,to_tsvector('ext.english_dict',tp.val) tsv
    ,val FROM test.tbl_plain tp
    ) tbl
    ) raw
    WHERE tsv @@ tsq ORDER BY ts_rank(tsv,tsq) DESC;

    Ожидаемый результат — выполнение без ошибок.

  2. Запустите блок кода (использование FTS с индексом GIN):

    EXPLAIN (ANALYZE,BUFFERS) SELECT
    id
    ,pattern
    ,tsv_txt
    ,tsq
    ,val
    FROM
    (SELECT
    ts_headline('ext.english_dict',val,tsq,'HighlightAll=true,StartSel=<<*,StopSel=*>>') pattern
    ,id
    ,regexp_split_to_array(tsv::text,' ') tsv_txt
    ,tsv
    ,tsq
    ,val
    FROM (SELECT
    id
    ,to_tsquery('ext.english_dict','database <2> server & !sql') tsq
    ,to_tsvector('ext.english_dict',tp.val) tsv
    ,val FROM test.tbl_gin tp
    ) tbl
    ) raw
    WHERE tsv @@ tsq ORDER BY ts_rank(tsv,tsq) DESC;

    В плане запроса присутствует шаг Recheck cond.

  3. Запустите блок кода (использование FTS с индексом RUM):

    EXPLAIN (ANALYZE,BUFFERS) SELECT
    id
    ,pattern
    ,tsv_txt
    ,tsq
    ,val
    FROM
    (SELECT
    ts_headline('ext.english_dict',val,tsq,'HighlightAll=true,StartSel=<<*,StopSel=*>>') pattern
    ,id
    ,regexp_split_to_array(tsv::text,' ') tsv_txt
    ,tsv
    ,tsq
    ,val
    FROM (SELECT
    id
    ,to_tsquery('ext.english_dict','database <2> server & !sql') tsq
    ,to_tsvector('ext.english_dict',tp.val) tsv
    ,val FROM test.tbl_rum tp
    ) tbl
    ) raw
    WHERE tsv @@ tsq ORDER BY ts_rank(tsv,tsq) DESC;

    В плане запроса отсутствует шаг Recheck cond. Стоимость запроса в шаге 3 меньше или равна стоимости запроса в шаге 2.

Влияние на производительность. Оценка стоимости дискового хранения

  1. Для таблицы tbl_rum добавьте индексы, использующие класс операторов rum_tsvector_hash_ops. Реиндексируйте таблицы:

    CREATE INDEX IF NOT EXISTS tbl_rum_to_tsvector_idx2 ON test.tbl_rum USING rum(to_tsvector('ext.english_dict'::regconfig, val) rum_tsvector_hash_ops);
    CREATE INDEX IF NOT EXISTS tbl_rum_to_tsvector_idx3 ON test.tbl_rum USING rum(to_tsvector('ext.english'::regconfig, val) rum_tsvector_hash_ops);
    REINDEX TABLE test.tbl_plain;
    REINDEX TABLE test.tbl_rum;
    REINDEX TABLE test.tbl_gin;

    Ожидаемый результат — выполнение без ошибок.

  2. Сравните размеры индексов для всех таблиц:

    SELECT
    c.oid::regclass::text
    ,c.relkind,pg_get_indexdef(c.oid)
    ,pg_relation_size(c.oid)
    FROM pg_class c
    JOIN pg_namespace ns ON c.relnamespace=ns.oid
    WHERE ns.nspname='test' ORDER BY 1,2;

    Размеры индексов ранжируются в порядке (от меньшего к большему):

    • GIN;
    • RUM (rum_tsvector_hash_ops);
    • RUM (превышает размер индекса GIN примерно в 2 раза. Превышает размер таблицы).

Ссылки на документацию разработчика

Дополнительно поставляемый модуль rum: https://github.com/postgrespro/rum.