2012-05-07 27 views
0

我有一個像200k元組的Postgresql表,所以沒那麼多。 我嘗試做的是過濾掉一些行,然後使用全文匹配命令他們:Postgresql - 整理集的全文排序

SELECT * FROM descriptions as d 
WHERE d.category_id = ? 
AND d.description != '' 
AND regexp_replace(d.description, '(...)', '') !~* '...' 
AND regexp_replace(d.description, '...', '') !~* '...' 
AND d.id != ? 
ORDER BY ts_rank_cd(to_tsvector('english', name), plainto_tsquery('english', 'my search words')) DESC LIMIT 5 OFFSET 0'; 

有上描述領域的GIN索引。

現在,只有在類別中少於4000條記錄的情況下,此查詢才能正常工作。當它更像5k或6k時,查詢變得非常緩慢。

我正在嘗試此查詢的不同變體。我注意到的是當我刪除WHERE子句或ORDER BY子句時,我得到了很大的加速。 (當然我得到不相關的結果)

我能做些什麼來加速這種組合?任何優化方式,或者我應該尋找Postgresql以外的解決方案?

其他問題:

我進一步實驗和例如這是我認爲運行速度太慢了簡單的查詢。我可以通過解釋分析告訴我們什麼時候使用主要指標,什麼時候沒有?

SELECT d.*, d.description <-> 'banana' as dist FROM descriptions as d ORDER BY dist DESC LIMIT 5 

"Limit (cost=16046.88..16046.89 rows=5 width=2425) (actual time=998.811..998.813 rows=5 loops=1)" 
" -> Sort (cost=16046.88..16561.90 rows=206010 width=2425) (actual time=998.810..998.810 rows=5 loops=1)" 
"  Sort Key: (((description)::text <-> 'banana'::text))" 
"  Sort Method: top-N heapsort Memory: 27kB" 
"  -> Seq Scan on products d (cost=0.00..12625.12 rows=206010 width=2425) (actual time=0.033..901.260 rows=206010 loops=1)" 
"Total runtime: 998.866 ms"` 

已回答(kgrittn):對於KNN-GiST,DESC關鍵字不正確,實際上不需要這裏。刪除它可以解決問題並提供正確的結果。

+0

你有什麼指數?查詢計劃者告訴你什麼? – 2012-05-07 16:16:39

+0

我做了杜松子酒和主要指標的實驗,但看起來他們沒有使用或什麼。如何看看規劃師告訴我什麼?解釋你的意思? – Lubiluk

回答

0

您查詢的輸出爲explain analyze會對您有所幫助。但我想這regexp_replace行是你的問題。 Postgres規劃人員無法知道有多少行可以匹配這兩行,因此它正在猜測和計劃基於這個有缺陷的問題的查詢。

我建議創建這樣的功能:

create function good_description(text) returns boolean as $$ 
    select 
    regexp_replace($1, '(...)', '') !~* '...' 
    and 
    regexp_replace($1, '...', '') !~* '...' 
$$ language sql immutable strict; 

而且使用該功能創建partial指數on expression

create index descriptions_good_description_idx 
    on good_description(description) 
    where description != ''; 

,然後在方式,讓Postgres一起使用查詢此指數:

SELECT * FROM descriptions as d 
WHERE d.category_id = ? 
AND d.description != '' 
AND good_description(d.description) 
AND d.id != ? 
ORDER BY ts_rank_cd(
    to_tsvector('english', name), 
    plainto_tsquery('english', 'my search words') 
) DESC 
LIMIT 5 OFFSET 0; 
+0

看起來你就在這裏。實際上,根據類別中描述的數量,EXPLAIN ANALYZE給了我不同的輸出。我認爲爲我的正則表達式創建索引是不可能的,因爲表達式是參數化的,並且使用了不同的版本,所以我想我只需要創建另一個表格來保存我的正則表達式過濾器的緩存結果,並在每次更改過濾規則時更新它們。 – Lubiluk

+0

where子句中的正則表達式過濾器的問題在於,在執行之前,即使大致預測有多少匹配也是完全不可能的。如果這些正則表達式的數量有限,你可以在這個表中添加額外的布爾列,如果正則表達式匹配或者不匹配,這些列可以使用觸發器輕鬆維護。而且他們不應該需要索引 - 數據庫自動維護的列統計信息應允許規劃人員根據其值進行過濾,從而正確估計匹配行的數量。這應該比索引更快。 – Tometzky

0

對於這種類型的應用程序,我們一直在從tsearch功能轉移到trigram功能;當你想選擇少量的最佳匹配時,速度要快得多。無論如何,這裏的人們通常更喜歡在文本搜索排名上的三元相似度匹配的語義。

http://www.postgresql.org/docs/current/interactive/pgtrgm.html

「借用」從編輯的問題,後來查詢格式化,以及包括索引創建語句,讓答案自足無評論筏:

CREATE INDEX descriptions_description_trgm 
    ON descriptions 
    USING gist (description gist_trgm_ops); 

SELECT d.*, d.description <-> 'banana' as dist 
    FROM descriptions as d 
    ORDER BY dist LIMIT 5; 

這個應該從「距離」序列中的GiST索引返回行,直到它到達LIMIT

+0

是的,沒錯。謝謝。 – Lubiluk

+0

我已經更新了基於這條評論流的答案,並刪除了我之前的評論,因爲它們現在已經包含在答案中。 (@Lubiluk,你可能也想這樣做。) – kgrittn