带有 LIMIT 的慢 ORDER BY

zir*_*iri 11 postgresql performance index full-text-search

我有这个查询:

SELECT * 
FROM location 
WHERE to_tsvector('simple',unaccent2("city"))
   @@ to_tsquery('simple',unaccent2('wroclaw')) 
order by displaycount
Run Code Online (Sandbox Code Playgroud)

我很高兴:

"Sort  (cost=3842.56..3847.12 rows=1826 width=123) (actual time=1.915..2.084 rows=1307 loops=1)"
"  Sort Key: displaycount"
"  Sort Method: quicksort  Memory: 206kB"
"  ->  Bitmap Heap Scan on location  (cost=34.40..3743.64 rows=1826 width=123) (actual time=0.788..1.208 rows=1307 loops=1)"
"        Recheck Cond: (to_tsvector('simple'::regconfig, unaccent2((city)::text)) @@ '''wroclaw'''::tsquery)"
"        ->  Bitmap Index Scan on location_lower_idx  (cost=0.00..33.95 rows=1826 width=0) (actual time=0.760..0.760 rows=1307 loops=1)"
"              Index Cond: (to_tsvector('simple'::regconfig, unaccent2((city)::text)) @@ '''wroclaw'''::tsquery)"
"Total runtime: 2.412 ms"
Run Code Online (Sandbox Code Playgroud)

但是当我添加 LIMIT 时,执行时间超过 2 秒:

SELECT * 
FROM location 
WHERE to_tsvector('simple',unaccent2("city"))
   @@ to_tsquery('simple',unaccent2('wroclaw')) 
order by displaycount 
limit 20
Run Code Online (Sandbox Code Playgroud)

解释:

"Limit  (cost=0.00..1167.59 rows=20 width=123) (actual time=2775.452..2775.643 rows=20 loops=1)"
"  ->  Index Scan using location_displaycount_index on location  (cost=0.00..106601.25 rows=1826 width=123) (actual time=2775.448..2775.637 rows=20 loops=1)"
"        Filter: (to_tsvector('simple'::regconfig, unaccent2((city)::text)) @@ '''wroclaw'''::tsquery)"
"Total runtime: 2775.693 ms"
Run Code Online (Sandbox Code Playgroud)

我认为这是 ORDER BY 和 LIMIT 的一些问题。如何强制 PostgreSQL 使用索引并在最后进行排序?

子查询没有帮助:

SELECT * 
FROM (
    SELECT * 
    FROM location 
    WHERE to_tsvector('simple',unaccent2("city"))
       @@ to_tsquery('simple',unaccent2('wroclaw')) 
    order by displaycount
) t 
LIMIT 20;
Run Code Online (Sandbox Code Playgroud)

或者:

SELECT * 
FROM (
    SELECT * 
    FROM location 
    WHERE to_tsvector('simple',unaccent2("city"))
       @@ to_tsquery('simple',unaccent2('wroclaw'))
) t 
order by displaycount 
LIMIT 20;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 13

我的猜测是这将解决您的查询:

SELECT * 
FROM   location 
WHERE     to_tsvector('simple',unaccent2(city))
       @@ to_tsquery('simple',unaccent2('wroclaw')) 
ORDER  BY to_tsvector('simple',unaccent2(city))
       @@ to_tsquery('simple',unaccent2('wroclaw')) DESC
         ,displaycount 
LIMIT  20;
Run Code Online (Sandbox Code Playgroud)

我重复该WHERE条件作为ORDER BY子句的第一个元素——这在逻辑上是多余的,但应该让查询规划器不要假设根据索引处理行会更好location_displaycount_index——结果证明它要贵得多。

潜在的问题是查询计划器显然严重错误地判断了您的WHERE条件的选择性和/或成本。我只能推测这是为什么。

您是否正在运行autovacuum - 它也应该负责ANALYZE在您的桌子上运行?因此,您的表统计数据是最新的吗?如果您运行任何效果:

ANALYZE location;
Run Code Online (Sandbox Code Playgroud)

然后再试一次?

也可能是@@操作员的选择性被误判了。我认为由于逻辑原因很难估计。


如果我的查询不应该解决问题,并且通常要验证基础理论,请执行以下两件事之一:

后者侵入性较小,仅影响当前会话。它使方法bitmap heap scan和方法保持bitmap index scan开放,由更快的计划使用。
然后重新运行查询。

顺便说一句:如果理论是合理的,那么您的查询(正如您现在所拥有的)将在 FTS 条件下使用选择性较少的搜索词快得多 - 与您可能期望的相反。尝试一下。