0

我有一个包含 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): DESC 关键字对于 KNN-GiST 不正确,实际上这里不需要它。删除它可以解决问题并给出正确的结果。

4

2 回答 2

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.

于 2012-05-07T16:45:08.347 回答
0

查询的输出explain analyze会有所帮助。但我想这regexp_replace条线是你的问题。Postgres 计划程序无法知道有多少行将匹配这两行,因此它正在根据这个有缺陷的问题猜测和计划查询。

我建议创建一个这样的函数:

create function good_description(text) returns boolean as $$
  select
   regexp_replace($1, '(...)', '') !~* '...' 
   and
   regexp_replace($1, '...', '') !~* '...'
$$ language sql immutable strict;

并使用此函数在表达式上创建部分索引:

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;
于 2012-05-07T22:01:45.893 回答