rum. Использование RUM индексов
В исходном дистрибутиве установлено по умолчанию: нет.
Связанные компоненты: отсутствуют.
Схема размещения:
ext
.
Индексный метод доступа RUM является одним из инструментов оптимизации доступа к данным. RUM основан на GIN-подобном индексном методе и включает дополнительную методику оценки релевантности результата поиска («дистанцией»).
Функциональность поставляется в качестве расширения в составе продукта, не требует специальных шагов по настройке со стороны сотрудников сопровождения. Активация расширения не требует изменения параметров конфигурации БД.
RUM имеет качественное преимущество в возможности ранжирования результатов поиска по «дистанции»/«схожести». При этом «дистанция»/«схожесть» имеют другой алгоритм обработки, чем существующие функции ts_rank
, ts_rank_cd
.
Для незначительного уменьшения размера индекса можно использовать классы операторов hash
, если не предполагается префиксного поиска.
RUM имеет количественное преимущество перед существующими индексными методами только при соблюдении следующих условий:
- Запрос SQL должен иметь условие поиска по дистанции. В случае FTS -
tsquery
должен использовать оператор дистанции«<=>»
,«<2>»
,«<3>»
и тому подобные. - Запрос 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;
Настройка
Настройка не требуется.
Использование модуля
Подготовительные действия
-
Создайте расширение
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; -
Создайте конфигурации 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; -
Скопируйте конфигурации со стеммером:
CREATE TEXT SEARCH CONFIGURATION ext.english (COPY=pg_catalog.english);
-
Создайте таблицу для тестов (вариант RUM):
CREATE TABLE test.tbl_rum (LIKE test.tbl_plain INCLUDING ALL);
-
Создайте 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 словарь
-
Запустите блок кода (использование 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;Ожидаемый результат — выполнение без ошибок.
-
Запустите блок кода (использование 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
. -
Запустите блок кода (использование 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.
Влияние на производительность. Оценка стоимости дискового хранения
-
Для таблицы
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;Ожидаемый результат — выполнение без ошибок.
-
Сравните размеры индексов для всех таблиц:
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.