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

Комментарий

Операторы вне классов операторов

<=>

tsvector

tsquery

Дистанция между операндами

timestamp

timestamp

-

int2

int2

-

int4

int4

-

int8

int8

-

float4

float4

-

float8

float8

-

money

money

-

oid

oid

-

anyarray

anyarray

-

=>

timestamp

timestamp

Дистанция только для операнда 2

timestamptz

timestamptz

-

int2

int2

-

int4

int4

-

int8

int8

-

float4

float4

-

float8

float8

-

money

money

-

oid

oid

-

<=

timestamp

timestamp

Дистанция только для операнда 1

timestamptz

timestamptz

-

int2

int2

-

int4

int4

-

int8

int8

-

float4

float4

-

float8

float8

-

money

money

-

oid

oid

-

%

anyarray

anyarray

Сходство массивов. Под сходством определяется:
наличие общих элементов массивов
размерность массивов соразмерна

rum_tsvector_ops

@@

tsvector

tsquery

Пересечение операндов

rum_tsquery_ops

@@

tsquery

tsvector

Пересечение операндов

<=>

Дистанция между операндарми

rum_tsvector_hash_ops

@@

tsvector

tsquery

Пересечение операндов.
Класс операторов не поддерживает префикс в FTS, но занимает ~ на 5% меньше места

<=>

Дистанция между операндами

rum_tsvector_addon_ops

@@

tsvector

tsquery

Пересечение операндов

rum_tsvector_hash_addon_ops

@@

tsvector

tsquery

Пересечение операндов.
Класс операторов не поддерживает префикс в FTS, но занимает ~ на 5% меньше места

rum_timestamp_ops

<

timestamp

timestamp

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_timestamptz_ops

timestamptz

timestamptz

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_int2_ops

<

int2

int2

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_int4_ops

<

int4

int4

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_int8_ops

<

int8

int8

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

-

<=

-

rum_float4_ops

<

float4

float4

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_float8_ops

<

float8

float8

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_money_ops

<

money

money

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_oid_ops

<

oid

oid

-

<=

-

=

-

>=

-

>

-

<=>

Дистанция между операндами

=>

Дистанция только для операнда 2

<=

Дистанция только для операнда 1

rum_time_ops

<

time

time

-

<=

-

=

-

>=

-

>

-

rum_timetz_ops

<

timetz

timetz

-

<=

-

=

-

>=

-

=

-

rum_date_ops

<

date

date

-

<=

-

=

-

>=

-

>

-

rum_interval_ops

<

interval

interval

-

<=

-

=

-

>=

-

>

-

rum_macaddr_ops

<

macaddr

macaddr

-

<=

-

=

-

>=

-

>

-

rum_inet_ops

<

inet

inet

-

<=

-

=

-

>=

-

>

-

rum_cidr_ops

<

cidr

cidr

-

<=

-

=

-

>=

-

>

-

rum_text_ops

<

text

text

-

<=

-

=

-

>=

-

>

-

rum_varchar_ops

<

varchar

varchar

-

<=

-

=

-

>=

-

>

-

rum_char_ops

<

char

char

-

<=

-

=

-

>=

-

>

-

rum_bytea_ops

<

bytea

bytea

-

<=

-

=

-

>=

-

>

-

rum_bit_ops

<

bit

bit

-

<=

-

=

-

>=

-

>

-

rum_varbit_ops

<

varbit

varbit

-

<=

-

=

-

>=

-

>

-

rum_numeric_ops

<

numeric

numeric

-

<=

-

=

-

>=

-

>

-

rum_anyarray_ops

&&

anyarray

anyarray

-

@>

-

<@

-

=

-

%

-

<=>

Дистанция между операндами

rum_anyarray_addon_ops

&&

anyarray

anyarray

Класс операторов не поддерживает определение сходства и дистанции

@>

-

<@

-

=

-

rum_tsvector_timestamp_ops

@@

tsvector

tsquery

Класс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamp

rum_tsvector_hash_timestamp_ops

@@

tsvector

tsquery

Класс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamp.Не поддерживает префикс в FTS, но занимает ~ на 5% меньше места

rum_tsvector_timestamptz_ops

@@

tsvector

tsquery

Класс операторов предназначен для раздельного хранения tsquery и поля, определяющего дистанцию. Дистанция хранится в поле с типом timestamptz

rum_tsvector_hash_timestamptz_ops

@@

tsvector

tsquery

Класс операторов предназначен для раздельного хранения 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_NORM

0x00

Длина документа не учитывается

RANK_NORM_LOGLENGTH

0x01

Ранг документа делится на сумму единицы и логарифма длины документа

RANK_NORM_LENGTH

0x02

Ранг документа делится на его длину

RANK_NORM_EXTDIST

0x04

Ранг документа делится на среднее гармоническое расстояние между блоками

RANK_NORM_UNIQ

0x08

Ранг документа делится на число уникальных слов в документе

RANK_NORM_LOGUNIQ

0x10

Ранг документа делится на сумму единицы и логарифма числа уникальных слов в документе

RANK_NORM_RDIVRPLUS1

0x20

Ранг делится на значение, которое на единицу больше самого ранга

Доработка#

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

Ограничения#

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

Установка#

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

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.