我有一个包含数百万条目的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
答案 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;