2017-02-13 83 views
10

我已閱讀Postgres發佈的docs/wiki中的功能索引和僅索引掃描。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只能識別函數索引的覆蓋索引,如果函數中使用的列正呼叫也包含

例如:

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」 作爲輸入 - 因此呈現我的索引 - 雖然它會覆蓋所有值 - 不能支持僅索引掃描。

這是否可以通過一種方式進行處理,從而爲索引掃描提供可能性?

@ 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)); 

第一LEA DS到:

"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只承認在函數式indizies上覆蓋indizies,您能提供對支持這些函數的文檔的任何參考;這非常直觀。那是......你現在怎麼知道。 –

+0

表「xmltable」的完整定義是什麼?如果指數存在,那麼聲明的執行計劃是什麼? Postgres是否將索引用於索引查找,還是根本不使用該索引? –

+0

我知道,因爲我嘗試過使用和不使用xml,普通表,json等等。它對我來說似乎也很奇怪 - 我將編輯這個問題來追加這個 – billdoor

回答

7

編輯決賽:爲什麼它是不可能

根據文檔:索引類型支持這一(即B樹一如既往地支持這個時候的PostgreSQL是indexonly做掃描,依據和SpGiST只針對一些特定的運營商,並GIN完全不能勝任)。並且可以從索引重建原始索引值。

第二個要求是最有趣的。

如果列很簡單(a, b)並且您的索引能夠重建原始存儲值。

而在功能使功能索引工作的情況下,您應該創建具有原始值的索引。這意味着(f1(a), f2(b))索引將再次出現在表格中,因爲您無法從這些值中重建索引數據(a, b)。開發人員提出的解決方法是在此情況下創建索引(f1(a), f2(b), a, b),因爲索引包含原始數據,因此查詢計劃程序可以確定是否可以運行僅索引掃描。

回到你的問題,創建索引只掃描xml列是不可能的:沒有運營商支持xml數據比較至關重要的btree。 xml數據沒有比較運算符的定義。因此您不能在任何類型的索引中使用此列,但是在僅索引掃描中需要它來提示查詢優化器執行僅索引掃描。

編輯:(解決方案如何實現僅索引掃描特定的XPath表達式)

如果你知道這些數據將被頻繁使用,我會建議,以解決通過觸發功能,並創造200這個問題更多的領域和索引覆蓋他們。類似的東西:

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表達式上創建函數索引相同。

原始響應:(可能是那些願意調整查詢優化有趣)

那麼問題是,你的查詢優化器認爲XPath函數調用是最簡單的。即它就像調用簡單的數學運算符一樣,其代價是1.在這種情況下,查詢優化器認爲從表中獲取並再次計算會更容易,然後執行純索引唯一掃描。

如果將xpath調用成本增加到可以說1000,查詢優化器將看到這樣的調用非常困難(實際上是這樣),並嘗試執行僅索引掃描。在我的測試設置我已經執行

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時)正在通過索引(idx_covering_index_3上的位圖索引掃描)運行,然後從表中獲取數據(xmltable上的位圖堆掃描)。第二個查詢(當xPath成本爲100時)僅通過索引(使用idx_covering_index_3的索引掃描)運行,並且不掃描該表。 –

+0

這不完全是你要求的(*索引只掃描*),但它可能是一個相當大的改進。所以我會贊成。 –