2016-08-30 57 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