2016-08-30 55 views
0

我從進口的所有http://www.geonames.org/表到我的本地的PostgreSQL數據庫9.5.3.0與像這樣的索引穿插它:慢查詢儘管指數

create extension pg_trgm; 
CREATE INDEX name_trgm_idx ON geoname USING GIN (name gin_trgm_ops); 
CREATE INDEX fcode_trgm_idx ON geoname USING GIN (fcode gin_trgm_ops); 
CREATE INDEX fclass_trgm_idx ON geoname USING GIN (fclass gin_trgm_ops); 
CREATE INDEX alternatename_trgm_idx ON alternatename USING GIN (alternatename gin_trgm_ops); 
CREATE INDEX isolanguage_trgm_idx ON alternatename USING GIN (isolanguage gin_trgm_ops); 
CREATE INDEX alt_geoname_id_idx ON alternatename (geonameid) 

現在我想查詢國名在不同的語言和交叉引用GEONAMES,象這樣這些備選名稱屬性:

select g.geonameid as geonameid ,a.alternatename as name,g.country as country, g.fcode as fcode 
from geoname g,alternatename a 
where 
     a.isolanguage=LOWER('de') 
     and a.alternatename ilike '%Sa%' 
     and (a.ishistoric = FALSE OR a.ishistoric IS NULL) 
     and (a.isshortname = TRUE OR a.isshortname IS NULL) 
     and a.geonameid = g.geonameid 
     and g.fclass='A' 
     and g.fcode ='PCLI'; 

不幸的是,雖然這個查詢時間只要13到15秒的八核心的機器上有一個快速的SSD。 「解釋分析冗長的」顯示了這個:

Nested Loop (cost=0.43..237138.04 rows=1 width=25) (actual time=1408.443..10878.115 rows=15 loops=1) 
    Output: g.geonameid, a.alternatename, g.country, g.fcode 
    -> Seq Scan on public.alternatename a (cost=0.00..233077.17 rows=481 width=18) (actual time=0.750..10862.089 rows=2179 loops=1) 
     Output: a.alternatenameid, a.geonameid, a.isolanguage, a.alternatename, a.ispreferredname, a.isshortname, a.iscolloquial, a.ishistoric 
     Filter: (((a.alternatename)::text ~~* '%Sa%'::text) AND ((a.isolanguage)::text = 'de'::text)) 
     Rows Removed by Filter: 10675099 
    -> Index Scan using pk_geonameid on public.geoname g (cost=0.43..8.43 rows=1 width=11) (actual time=0.006..0.006 rows=0 loops=2179) 
     Output: g.geonameid, g.name, g.asciiname, g.alternatenames, g.latitude, g.longitude, g.fclass, g.fcode, g.country, g.cc2, g.admin1, g.admin2, g.admin3, g.admin4, g.population, g.elevation, g.gtopo30, g.timezone, g.moddate 
     Index Cond: (g.geonameid = a.geonameid) 
     Filter: ((g.fclass = 'A'::bpchar) AND ((g.fcode)::text = 'PCLI'::text)) 
     Rows Removed by Filter: 1 

這對我來說似乎表明,莫名其妙的順序掃描481行(我認爲是相當低)進行,但仍然需要很長。我目前無法理解這一點。有任何想法嗎?

+0

的Postgres低估的行數爲'alternatename'。顯然,'alternatename.alternatename'上的索引沒有被使用。你運行'分析備用名'來更新統計數據嗎? 'ishistoric'和'isshortname'的條件從表中刪除多少行?也許如果你創建一個組合索引可能有幫助,或者只是從結果中刪除更多行。如果你可以擺脫布爾列中的NULL值,這可能會有所幫助,這樣你就不需要'或者是null'(這很難正確索引) –

+1

與性能無關:你應該真的停止使用舊的,在where子句中過時和脆弱的隱式連接,並使用明確的JOIN運算符 –

+0

我沒有對結果進行分析。如果我從等式中刪除ishistoric和isshortname,它會給我一個2秒的提升。 – keyboardsamurai

回答

2

如果您正在搜索的最少3個字符%Sa%將不起作用,trigrams將工作,%foo%將。但是,您的索引仍然不夠好。根據什麼參數是動態使用多列或過濾索引:

CREATE INDEX jkb1 ON geoname(fclass, fcode, geonameid, country); 
CREATE INDEX jkb2 ON geoname(geonameid, country) WHERE fclass = 'A' AND fcode = 'PCLI'; 

同爲其他表:

CREATE INDEX jkb3 ON alternatename(geonameid, alternatename) WHERE (a.ishistoric = FALSE OR a.ishistoric IS NULL) 
     AND (a.isshortname = TRUE OR a.isshortname IS NULL) AND isolanguage=LOWER('de') 
+1

當然你是對的。使用參數化索引選擇時間降至25ms以下,而不帶參數時間爲120ms。非常感謝。 – keyboardsamurai