使用通配符进行慢速连接查询

时间:2014-06-11 20:13:24

标签: sql postgresql join

我有一个包含数百万条目的keyword表。它通过多对多关系链接到element表。我想获得与关键字匹配的所有元素ID。我尝试了这个查询,没有问题,它在几毫秒内返回行。

SELECT element_id FROM element_keyword 
JOIN keyword ON keyword.id = element_keyword.keyword_id
WHERE keyword.value like 'LOREM';   

执行计划

"Nested Loop  (cost=278.50..53665.56 rows=65 width=4)"
"  ->  Index Scan using keyword_value_index on keyword  (cost=0.43..8.45 rows=1 width=4)"
"        Index Cond: ((value)::text = 'LOREM'::text)"
"        Filter: ((value)::text ~~ 'LOREM'::text)"
"  ->  Bitmap Heap Scan on element_keyword  (cost=278.07..53510.66 rows=14645 width=8)"
"        Recheck Cond: (keyword_id = keyword.id)"
"        ->  Bitmap Index Scan on element_keyword_keyword_index  (cost=0.00..274.41 rows=14645 width=0)"
"              Index Cond: (keyword_id = keyword.id)"

然而,当我在搜索字符串末尾放置一张外卡时,请求变得非常慢。 (〜60000毫秒)

SELECT element_id FROM element_keyword 
JOIN keyword ON keyword.id = element_keyword.keyword_id
WHERE keyword.value like 'LOREM%';          

执行计划:

"Hash Join  (cost=12.20..3733738.08 rows=19502 width=4)"
"  Hash Cond: (element_keyword.keyword_id = keyword.id)"
"  ->  Seq Scan on element_keyword  (cost=0.00..3002628.08 rows=194907408 width=8)"
"  ->  Hash  (cost=8.45..8.45 rows=300 width=4)"
"        ->  Index Scan using keyword_value_index on keyword  (cost=0.43..8.45 rows=300 width=4)"
"              Index Cond: (((value)::text ~>=~ 'LOREM'::text) AND ((value)::text ~<~ 'LOREN'::text))"
"              Filter: ((value)::text ~~ 'LOREM%'::text)"

即使通配符没有提供更多结果,查询也很慢。

我在关键字(值)和element_keyword(keyword_id)

上创建了一个索引
CREATE INDEX "keyword_value_index" ON "keyword" (value text_pattern_ops);
CREATE INDEX "element_keyword_keyword_index" ON "element_keyword" (keyword_id);

背后真的发生了什么?我怎么能解决它?

更新

我不确定这是否会有所帮助,但还需要进行一些测试:

select id from keyword where value like 'LOREM%'; 
-> 6 ids retrieved in 17ms 

select * from element_keyword where keyword_id in (1961746,1961710,2724258,2121442,1633163,1026116); 
-> 40 rows retrieved in 17ms

select * from element_keyword where keyword_id in (select id from keyword where value like 'LOREM%');
-> 40 rows in 63221 ms

3 个答案:

答案 0 :(得分:2)

原因是:

WHERE keyword.value like 'LOREM'

LIKE运算符的无意义用例。没有通配符(或转义字符),这实际上与:

相同
WHERE keyword.value = 'LOREM'

..可以使用索引进行相等 - 因此是普通B树索引

如果您对匹配前导字符(左锚定搜索模式)感到满意,那么带有运算符类 text_pattern_ops 的B树索引将为您提供良好的服务。详细信息:
Full-text search in CouchDB

对于任意模式匹配,请使用 pg_trgm 模块:
PostgreSQL LIKE query performance variations

Full text search可能是也可能不是你想要的。它基于字典和词干,而不是像你的例子所暗示的文本模式。

Overview over pattern matching in Postgres.

此外,keyword (value,id)上的多列索引(列的顺序相关)可启用仅索引扫描:
SQL query for index/primary key ordinal
The Postgres Wiki on index-only scan

答案 1 :(得分:1)

我相信你需要一个* _pattern_ops索引(见http://www.postgresql.org/docs/9.3/static/indexes-opclass.html):

create index pattern_idx on keyword(value text_pattern_ops)

答案 2 :(得分:0)

基本上发生的是第二个查询正在进行顺序扫描(出于某些原因我不明白)。这种顺序扫描需要花费很长时间。

禁用顺序扫描会强制查询使用索引。因此,如果我在查询之前执行此行,则会变得非常快。

set enable_seqscan to off;