Langsame Bestellung mit LIMIT

11

Ich habe diese Frage:

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

Ich bin zufrieden damit:

"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"

Wenn ich jedoch LIMIT hinzufüge, dauert die Ausführung länger als 2 Sekunden:

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

Erklären:

"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"

Ich denke, dass es ein Problem mit ORDER BY und LIMIT ist. Wie kann ich PostgreSQL zwingen, den Index zu verwenden und die Bestellung am Ende durchzuführen?

Unterabfrage hilft nicht:

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

oder:

SELECT * 
FROM (
    SELECT * 
    FROM location 
    WHERE to_tsvector('simple',unaccent2("city"))
       @@ to_tsquery('simple',unaccent2('wroclaw'))
) t 
order by displaycount 
LIMIT 20;
ziri
quelle

Antworten:

12

Ich vermute, dass dies Ihre Anfrage korrigieren würde:

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;

Ich wiederhole die WHEREBedingung als erstes Element der ORDER BYKlausel - die logisch redundant ist, aber den Abfrageplaner davon abhalten sollte anzunehmen, dass es besser wäre, Zeilen gemäß dem Index zu verarbeiten location_displaycount_index-, was sich als viel teurer herausstellt.

Das zugrunde liegende Problem besteht darin, dass der Abfrageplaner die Selektivität und / oder die Kosten Ihres WHEREZustands offensichtlich grob falsch einschätzt . Ich kann nur spekulieren, warum das so ist.

Haben Sie Autovakuum- Betrieb - der sich auch um das Laufen ANALYZEauf Ihren Tischen kümmern sollte ? Sind Ihre Tabellenstatistiken dabei aktuell? Jeder Effekt, wenn Sie ausführen:

ANALYZE location;

Und versuche es erneut?

Es kann auch sein, dass die Selektivität des @@Bedieners falsch eingeschätzt wird. Ich würde mir vorstellen, dass es aus logischen Gründen sehr schwer zu schätzen ist.


Wenn meine Abfrage das Problem nicht lösen und die zugrunde liegende Theorie im Allgemeinen überprüfen soll, führen Sie eines der beiden folgenden Dinge aus:

Letzteres ist weniger aufdringlich und wirkt sich nur auf die aktuelle Sitzung aus. Es lässt die Methoden bitmap heap scanund bitmap index scanoffen, die vom schnelleren Plan verwendet werden.
Führen Sie dann die Abfrage erneut aus.

Übrigens: Wenn die Theorie stichhaltig ist, wird Ihre Abfrage (wie Sie sie jetzt haben) mit einem weniger selektiven Suchbegriff in der FTS-Bedingung viel schneller sein - entgegen Ihren Erwartungen. Versuch es.

Erwin Brandstetter
quelle
1
Die Abfrage funktioniert. Das Ausschalten des Indexscans funktioniert auch. ANALYSE funktioniert nicht. Vielen Dank für die umfassende Antwort.
Ziri
0

Wenn Sie eine LIMIT-Postgresql-Anpassung verwenden, ist der Plan optimal, um nur die Teilmenge der Zeile abzurufen. Leider trifft es in Ihrem Fall irgendwie eine falsche Wahl. Dies kann daran liegen, dass die Statistiken für die Tabelle zu alt sind. Versuchen Sie, die Statistik zu aktualisieren, indem Sie den Speicherort VACUUM ANALYZE angeben.

Das Erzwingen der Verwendung von Indizes erfolgt normalerweise, indem die Verwendung von sequentiellen Scans nicht zugelassen wird (setzen Sie enable_seqscan = false). In Ihrem Fall wird jedoch kein sequentieller Scan durchgeführt, sondern nur mit LIMIT zu einem anderen Index für die Abfrage gewechselt.

Falls die Analyse nicht hilft, können Sie dann feststellen, welche Version von postgresql Sie verwenden? Wie viele Zeilen enthält die Tabelle?

Eelke
quelle
Analyse hat nicht geholfen. Die Tabelle hat ungefähr 36000 Zeilen und ich verwende postgresql 9.1.
Ziri