我有一个类似200k元组的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之外寻找解决方案吗?
我正在进一步尝试,例如,这是我认为运行得太慢的最简单的查询。我可以从解释中分析它何时使用gist索引以及何时不使用?
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 :(得分: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
。
答案 1 :(得分:0)
您的查询的explain analyze
输出会有所帮助。但我想这regexp_replace
行就是你的问题。 Postgres规划器只是不知道有多少行会匹配这两行,所以它正在猜测并根据这个有缺陷的quess计划一个查询。
我建议创建一个这样的函数:
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;