2014-01-27 10 views
19

Meine Abfragen werden sehr langsam, wenn ich eine limit 1 hinzufügen.PostgreSQL Abfrage sehr langsam mit Limit 1

Ich habe eine Tabelle object_values mit timestamped Werte für Objekte:

timestamp | objectID | value 
-------------------------------- 
2014-01-27|  234 | ksghdf 

pro Objekt, das ich den letzten Wert erhalten möchten:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC LIMIT 1; 

(I annulliert die Abfrage nach mehr als 10 Minuten)

Diese Abfrage ist sehr langsam, wenn für eine gegebene objectID keine Werte vorhanden sind (sie ist schnell, wenn Ergebnisse vorhanden sind). Wenn ich die Grenze entfernen Sie es mich fast sofort sagt, dass es keine Ergebnisse:

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC; 
... 
Time: 0.463 ms 

Ein erklären, zeigt mir, dass die Abfrage ohne Limit den Index verwendet, wo, wie die Abfrage mit limit 1 nicht die Verwendung des Index machen :

langsam query:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 1; 
QUERY PLAN` 
---------------------------------------------------------------------------------------------------------------------------- 
Limit (cost=0.00..2350.44 rows=1 width=126) 
-> Index Scan Backward using object_values_timestamp on object_values (cost=0.00..3995743.59 rows=1700 width=126) 
    Filter: (objectID = 53708)` 

Schnelle Abfrage:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC; 
                QUERY PLAN 
-------------------------------------------------------------------------------------------------------------- 
Sort (cost=6540.86..6545.11 rows=1700 width=126) 
    Sort Key: timestamp 
    -> Index Scan using object_values_objectID on working_hours_t (cost=0.00..6449.65 rows=1700 width=126) 
     Index Cond: (objectID = 53708) 

Die Tabelle enthält 44.884.559 Zeilen und 66.762 eindeutige ObjectIDs.
Ich habe separate Indizes für beide Felder: timestamp und objectID.
Ich habe eine vacuum analyze auf dem Tisch gemacht und ich habe die Tabelle neu indiziert.

Zusätzlich wird die langsame Abfrage wird schnell, wenn ich die Grenze bis 3 oder höher:

explain SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp DESC limit 3; 
                QUERY PLAN 
-------------------------------------------------------------------------------------------------------------------- 
Limit (cost=6471.62..6471.63 rows=3 width=126) 
    -> Sort (cost=6471.62..6475.87 rows=1700 width=126) 
     Sort Key: timestamp 
     -> Index Scan using object_values_objectID on object_values (cost=0.00..6449.65 rows=1700 width=126) 
       Index Cond: (objectID = 53708) 

Im Allgemeinen Ich nehme an, es hat mit den Planern machen falsche Annahmen über die exectution Kosten und damit wählt für eine zu tun langsamer Ausführungsplan.

Ist das der wahre Grund? Gibt es dafür eine Lösung?

Antwort

23

Sie stoßen auf ein Problem, das sich auf das Fehlen von Statistiken über Zeilenkorrelationen bezieht. Überlegen Sie, ob Sie es als Referenz an pg-bugs melden, wenn Sie die neueste Postgres-Version verwenden.

Die Interpretation, die ich für Ihre Pläne vorschlagen würde, ist:

  • limit 1 macht Postgres für eine einzelne Zeile suchen, und so übernimmt dabei das, dass Ihr object_id üblich ist genug, dass es vernünftig erscheinen würde schnell in einem Index-Scan.

    Basierend auf den Statistiken, die Sie wahrscheinlich gab, ist es, dass es ~ 70 Zeilen im Durchschnitt lesen müssen, um eine Zeile zu finden, die passt; es merkt einfach nicht, dass object_id und timestamp mit dem Punkt korrelieren, an dem es tatsächlich einen großen Teil der Tabelle liest.

  • limit 3 Im Gegensatz dazu ist ihm klar, dass es genug ungewöhnlich ist, so ist es die Auffassung, ernst (und endet ...) Top-n eine erwarteten 1700 Zeilen Sortierung mit dem object_id Sie wollen, aus Gründen, dass dies wahrscheinlich billiger .

    Zum Beispiel könnte es wissen, dass die Verteilung dieser Zeilen so ist, dass sie alle im selben Bereich auf der Festplatte gepackt sind.

  • nein limit Klausel bedeutet, dass es die 1700 sowieso holen wird, so dass es direkt für den Index auf object_id geht.

Lösung, btw: einen Index für (object_id, timestamp) oder (object_id, timestamp desc) hinzufügen.

+0

Für den 'Limit 1' Fall meinst du Table Scan? Sie haben index scan geschrieben – harmic

+0

@harmic: OP hat einen Index-Scan dort ... nicht unbedingt von der ganzen Tabelle, aber sicherlich von viel mehr davon als was PG dachte. –

+0

Sie haben Recht! Ich habe nur den OP-Text gelesen, in dem er gesagt hat, dass er den Index nicht benutzt. Aber es wählt den Timestamp-Index aus; Seltsame Wahl – harmic

18

Sie können dieses Problem vermeiden, indem Sie der Abfrage eine nicht benötigte ORDER BY-Klausel hinzufügen.

SELECT * FROM object_values WHERE (objectID = 53708) ORDER BY timestamp, objectID DESC limit 1; 
+1

HA! Das ist fantastisch! Komplett repariert es! – BrianC

+1

Diese Antwort funktioniert tatsächlich, anders als die Antwort und alle obigen Kommentare. – mianos

+0

Das ist erstaunlich! Erhöhen Sie einfach meine Abfrage und können Sie sie in Runtime verwenden. Vielen Dank! –