2017-02-13 1 views
10

Я прочитал о функциональных индексах и индексах только в документах/вики, опубликованных Postgres.postgres 9.6 индекс только сканирование по функциональному индексу логически возможно, но не выполнено

теперь у меня есть запрос типа:

SELECT(xpath('/document/uuid/text()', xmldata))[1]::text, 
     (xpath('/document/title/text()', xmldata))[1]::text 
FROM xmltable 
WHERE(xpath('/document/uuid/text()', xmldata))[1]::text = 'some-uuid-xxxx-xxxx' 

и индекс:

CREATE INDEX idx_covering_index on xmltable using btree (
    ((xpath('/document/uuid/text()', xmldata))[1]::text),  
    ((xpath('/document/title/text()', xmldata))[1]::text) 
) 

Этот индекс, глядя на него логически, индекс покрытия и должен позволить индексному-только сканирование , так как все запрошенные значения содержатся в индексе (uuid и title)

Теперь я знаю, что Postgres распознает только индексы покрытия на функциональных индексах, если столбцы, используемые в функции-cal Ls также содержатся

например .:

SELECT to_upper(column1) from table where id >10 

1) не могут быть покрыты по этому показателю:

CREATE INDEX idx_covering_index on xmltable using btree (id, to_upper(column1)); 

2), но может быть покрыта этой одной:

CREATE INDEX idx_covering_index on xmltable using btree (column1, id, to_upper(column1)); 

, что приводит к индексированию только сканирования.

Если теперь я стараюсь это с моей настройкой XML:

CREATE INDEX idx_covering_index on xmltable using btree (xmldata, 
    ((xpath('/document/uuid/text()', xmldata))[1]::text),  
    ((xpath('/document/title/text()', xmldata))[1]::text) 
) 

Я получаю сообщение об ошибке:

data type xml has no default operator class for access method "btree"

справедливой достаточно, к сожалению, как правило, используется "text_ops" или "text_pattern_ops" не принимает "XML", как input - таким образом, мой индекс - хотя он будет охватывать все значения - не может поддерживать индексирование только.

Можно ли это обработать таким образом, чтобы обеспечить возможность сканирования только по индексу?

@ EDIT1:

Я знаю, что Postgres не может использовать индекс видел в 1) в качестве покрытия индекса, но может использовать индекс, как 2)

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

create table test (
    id serial primary key, 
    quote text 
) 



insert into test (number, quote) values ('I do not know any clever quotes'); 
insert into test (number, quote) values ('I am sorry'); 



CREATE INDEX idx_test_functional on test using btree ((regexp_replace(quote, '^I ', 'BillDoor '))); 
set enable_seqscan = off; 

analyze test; 

explain select quote from test where regexp_replace(quote, '^I ', 'BillDoor ') = 'BillDoor do not know any clever quotes' 

--> "Index Scan using idx_test_functional on test (cost=0.13..8.15 rows=1 width=27)" 

drop index idx_test_functional; 
CREATE INDEX idx_test_functional on test using btree (quote, (regexp_replace(quote, '^I ', 'BillDoor '))); 

analyze test; 

explain select quote from test where regexp_replace(quote, '^I ', 'BillDoor ') = 'BillDoor do not know any clever quotes' 

--> "Index Only Scan using idx_test_functional on test (cost=0.13..12.17 rows=1 width=27)" 

@ EDIT2:

Полное определение таблице XMLTABLE:

id serial primary key (clustered), 
xmldata xml (only data used to filter queries) 
history xml (never queried or read, just kept in case of legal inquiry) 
fileinfo text (seldom quieried, sometimes retrieved) 
"timestamp" timestamp (mainly for legal inquiries too) 

В таблице приведены около .: 500.000 записей, размер xmldata составляет от 350 до 800 байт, история намного больше, но редко извлекается и никогда не используется в фильтрах.

Для записи, чтобы получить реальные результаты, я всегда побежал analyze xmltable после того, как создал или сбросил индекс

полный план выполнения для запроса:

explain analyze select (xpath('/document/uuid/text()', d.xmldata))[1]::text as uuid 
from xmltable as d 
where 
(xpath('/document/uuid/text()', d.xmldata))[1]::text = 'some-uuid-xxxx-xxxx' and (xpath('/document/genre/text()', d.xmldata))[1]::text = 'bio' 

охватываемого этого indizies:

create index idx_genre on xmltable using btree (((xpath('/document/genre/text()', xmldata))[1]::text)); 

create index idx_uuid on xmltable using btree (((xpath('/document/uuid/text()', xmldata))[1]::text)); 

create index idx_uuid_genre on xmltable using btree (((xpath('/document/uuid/text()', xmldata))[1]::text), ((xpath('/document/genre/text()', xmldata))[1]::text)); 

приводит сначала чтобы:

"Index Scan using idx_genre on xmldata d (cost=0.42..6303.05 rows=18154 width=32)" 
" Index Cond: (((xpath('/document/genre/text()'::text, xmldata, '{}'::text[]))[1])::text = 'bio'::text)" 
" Filter: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)" 

достаточно справедливо я думал, только для тестирования я заставлю его использовать - в моем сознании - индекс покрытия:

drop index idx_uuid; 
drop index idx_genre; 

и теперь я получаю:

"Bitmap Heap Scan on xmltable d (cost=551.13..16025.51 rows=18216 width=32)" 
" Recheck Cond: ((((xpath('/document/genre/text()'::text, xmldata, '{}'::text[]))[1])::text = 'bio'::text) AND (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text))" 
" -> Bitmap Index Scan on idx_uuid_genre (cost=0.00..546.58 rows=18216 width=0)" 
"  Index Cond: ((((xpath('/document/genre/text()'::text, xmldata, '{}'::text[]))[1])::text = 'bio'::text) AND (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text))" 

Я также попытался переключить позиции uuid и жанра в индекс, тот же план выполнения.

+0

«Теперь я знаю, что postgres распознает только маскировки инклюзив на функциональных функциях, если также используются столбцы, в которые встроены функции-вызовы», можете ли вы предоставить любую ссылку на документацию, подтверждающую это; это очень противоречиво. Это ... как вы «теперь знаете». –

+0

Каково полное определение таблицы 'xmltable'? Каков план выполнения для оператора, если индекс присутствует? Использует ли Postgres индекс для поиска индекса или вообще не использует этот индекс? –

+0

Я знаю, потому что я пробовал с и без xml, обычных таблиц, json и т. Д. Мне также показалось странным - я отредактирую вопрос, чтобы добавить это – billdoor

ответ

7

EDIT FINAL: Почему нельзя

Согласно документации: PostgreSQL может сделать indexonly сканирование, когда тип индекса поддерживает это (т.е. ВТКЕЙ всегда поддерживает это, GiST и SpGiST только для некоторых конкретных операторов и GIN вообще не способен). И можно восстановить исходное индексированное значение из индекса.

Второе требование - самое интересное.

В случае столбцов это просто (a, b), и ваш индекс может восстановить исходное сохраненное значение.

И в случае функций для работы функционального индекса вы должны создать индекс, имеющий исходные значения. Это означает, что индекс (f1(a), f2(b)) снова появится в таблице, потому что вы не можете восстановить индексированные данные (a, b) из этих значений. Предлагаемое разработчиками решение - создать индекс (f1(a), f2(b), a, b), в этом случае планировщик запросов сможет определить, что можно запустить проверку только для индекса, потому что индекс содержит исходные данные.

И, вернувшись к вашему вопросу, для создания индекса только сканирование в столбце xml невозможно: нет операторов для поддержки сопоставления данных xml, имеющих решающее значение для btree. Не существует определения операторов сравнения для данных xml. и поэтому вы не можете использовать этот столбец в любом виде индекса, но вам нужно его в вашем индексировании только для проверки, чтобы подсказка оптимизатора запросов выполняла только проверку по индексу.

EDIT: (решение, как достичь индекса только сканировать на конкретных выражений XPath)

Если вы знаете, что эти данные будут использоваться часто, я рекомендовал бы, чтобы решить эту проблему с помощью функции запуска и создания 2 больше полей и покрыть их индексом.Нечто подобное:

ALTER TABLE public.xmltable ADD COLUMN xpath_uuid character varying(36); 
ALTER TABLE public.xmltable ADD COLUMN xpath_title character varying(100); 


CREATE INDEX idx_covering_materialized_xml_data 
    ON public.xmltable 
    USING btree 
    (xpath_uuid COLLATE pg_catalog."default", xpath_title COLLATE pg_catalog."default"); 

CREATE OR REPLACE FUNCTION public.introduce_xml_materialization() 
    RETURNS trigger AS 
$BODY$BEGIN 

NEW.xpath_uuid = (xpath('/document/uuid/text()', NEW.xmldata))[1]::text; 
NEW.xpath_title = (xpath('/document/title/text()', NEW.xmldata))[1]::text; 

RETURN NEW; 
END;$BODY$ 
    LANGUAGE plpgsql STABLE 
    COST 100; 



CREATE TRIGGER index_xml_data 
    BEFORE INSERT OR UPDATE 
    ON public.xmltable 
    FOR EACH ROW 
    EXECUTE PROCEDURE public.introduce_xml_materialization(); 

, а затем вы можете сделать просто:

SELECT xpath_uuid, xpath_title 
    FROM public.xmltable 
    where xpath_uuid = ' uuid1 ' 

который покажет вам индекс только сканировать:

"Index Only Scan using idx_covering_materialized_xml_data on xmltable (cost=0.14..8.16 rows=1 width=308)" 
" Index Cond: (xpath_uuid = ' uuid1 '::text)" 

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

ORIGINAL РЕПЛИКА: (может быть интересно для тех, кто хочет настроить оптимизатор запросов)

Ну проблема в том, что ваш оптимизатор запросов считает, что вызов функции XPATH проще. т. е. как вызов простого математического оператора, а его стоимость равна 1. В этом случае оптимизатор запросов думает, что его легче извлечь из таблицы и вычислить еще раз, а затем просто проверить только индекс.

ЕСЛИ вы увеличиваете стоимость вызова xpath, чтобы сказать, что оптимизатор запросов увидит, что такой вызов значительно сложнее (это на самом деле правда) и попытается выполнить проверку только по индексу. В моей тестовой конфигурации я казнил

update pg_proc set procost=1 where proname='xpath'; 

и план выполнения является

"Bitmap Heap Scan on xmltable (cost=4.17..11.30 rows=3 width=64)" 
" Recheck Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)" 
" -> Bitmap Index Scan on idx_covering_index_3 (cost=0.00..4.17 rows=3 width=0)" 
"  Index Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)" 

Но когда я

update pg_proc set procost=1000 where proname='xpath'; 

План выполнения переходит к индексу только сканировать

"Index Scan using idx_covering_index_3 on xmltable (cost=0.15..31.20 rows=3 width=64)" 
" Index Cond: (((xpath('/document/uuid/text()'::text, xmldata, '{}'::text[]))[1])::text = 'some-uuid-xxxx-xxxx'::text)" 

И на моем томе (т.е. , нет данных) минимальная стоимость запроса только по индексу значительно меньше, чем в исходном индексе + сканирование таблицы, при этом максимальная стоимость больше. Таким образом, для вас, чтобы обмануть запрос, может потребоваться установить еще более высокие значения стоимости вызова xpath.

Надеюсь, это поможет, и из любопытства просто покажите нам преимущества использования запросов только по индексу.

+0

плана запроса в вашем примере не переключается на сканирование только по индексу, а на индексное сканирование - может быть, недоразумение? – billdoor

+0

просто посмотрите: первый запрос (когда стоимость xPath равен 1) выполняется через индекс (Bitmap Index Scan на idx_covering_index_3), а затем извлекает данные из таблицы (Bitmap Heap Scan на xmltable). Второй запрос (когда стоимость xPath равен 100) выполняется только через индекс (Index Scan с использованием idx_covering_index_3) и не сканирует таблицу. –

+0

Это не совсем то, о чем вы просили (* Index Only Scan *), но это может быть довольно хорошим. Так что я продолжу. –