Langsame Index-Scans in einer großen Tabelle


11

Mit PostgreSQL 9.2 habe ich Probleme mit langsamen Abfragen in einer relativ großen Tabelle (mehr als 200 Millionen Zeilen). Ich versuche nichts Verrücktes, sondern füge nur historische Werte hinzu. Unten finden Sie die Abfrage und die Ausgabe des Abfrageplans.

Mein Tabellenlayout:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

Die Daten reichen vom 01.01.2012 bis jetzt, wobei ständig neue Daten hinzugefügt werden. Der Fremdschlüssel enthält ungefähr 2,2.000 unterschiedliche Werte prop_id, die gleichmäßig verteilt sind.

Ich stelle fest, dass die Zeilenschätzungen nicht weit entfernt sind, aber die Kostenschätzungen um den Faktor 4x größer erscheinen. Dies ist wahrscheinlich kein Problem, aber kann ich etwas dagegen tun?

Ich gehe davon aus, dass der Festplattenzugriff das Problem sein könnte, da sich die Tabelle nicht immer im Speicher befindet.

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

Irgendwelche Vorschläge, wie man das schneller macht?
Mir geht es auch gut, wenn ich nur höre, dass ich nichts Seltsames getan habe.


1
Bitte teilen Sie uns mit, wie Ihre Tabelle aussieht, welche Indizes sie enthält und wie sich die Daten verbreiten.
Colin 't Hart

Ich habe die von Ihnen angeforderten zusätzlichen Informationen hinzugefügt. Keine Ahnung, ob ich etwas verpasst habe.
Exelian

2
Seltsam: Ihre Erklärungsanalyse zeigt prop_time_idx, aber die Tabellendefinition zeigt entry_prop_id_timestamp_idx. Ist das der gleiche Index? Bitte repariere.
Colin 't Hart

Wenn Sie mit "Die Kostenschätzungen scheinen um den Faktor 4 größer zu sein" auf die Tatsache verweisen, dass die Kostenzahlen etwa das Vierfache der tatsächlichen Zeit betragen , beachten Sie bitte, dass die beiden nichts miteinander zu tun haben. Die Kosten sind nur eine Schätzung, die dem Abfrageoptimierer hilft, den am besten aussehenden Plan auszuwählen. Außerhalb dieses Kontextes ist es normalerweise ein bedeutungsloser Wert.
Dekso

1
Wie viele Prozent der Tabelle repräsentiert Ihr Datumsbereich (ohne Berücksichtigung der Werte für prop)? Wenn nur ein kleiner Prozentsatz, wäre vielleicht ein Index ("timestamp", prop)besser. propOft sind auch mehrere Indizes mit denselben führenden Spalten ( in Ihrem Fall) redundant.
Colin 't Hart

Antworten:


9

Ihre Tabelle ist groß , ebenso wie jeder Index, der sich über die gesamte Tabelle erstreckt. Vorausgesetzt, dass:

  • Es werden nur neue Daten (mit timestamp = now()) eingegeben
  • Bestehende Zeilen werden weder geändert noch gelöscht.
  • Sie haben Daten seit dem 01.01.2012, aber Anfragen beziehen sich überwiegend auf das aktuelle Jahr (?)

Ich würde einen mehrspaltigen Teilindex (Covering!) Vorschlagen :

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

Geben Sie nur den Zeitraum an, der regelmäßig abgefragt wird. Die Wirksamkeit verschlechtert sich mit der Zeit mit neuen Einträgen. Erstellen Sie den Index von Zeit zu Zeit neu. (Möglicherweise müssen Sie Ihre Abfragen anpassen.) Siehe verknüpfte Antwort unten.

Der letzte Spaltenwert ist nur enthalten, um nur Index-Scans daraus zu erhalten. Eine aggressive Autovakuumeinstellung kann hilfreich sein, indem die Sichtbarkeitskarte auf dem neuesten Stand gehalten wird, wie bereits bei @jjanes erwähnt .

Der Teilindex sollte leichter in den Arbeitsspeicher passen und länger dort bleiben.

Möglicherweise müssen Sie diese WHEREBedingung in Abfragen aufnehmen, damit der Planer versteht, dass der Index auf die Abfrage anwendbar ist, z.

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

Da Ihre Abfrage viele Zeilen summiert ( rows=13578), wird dies selbst bei einem Nur-Index-Scan einige Zeit dauern. Es sollte jedoch nicht annähernd 50 Sekunden dauern. Weniger als eine Sekunde auf halbwegs anständiger Hardware.

Verwandte (aber ignorieren CLUSTERund FILLFACTOR, beide sind irrelevant, wenn Sie nur Index-Scans daraus erhalten können) :

Nebenbei:
Da Sie derzeit einen Index für haben (prop_id, "timestamp"), kann der zusätzliche Index für (prop_id)mehr kosten, als er wert ist:


Wäre das hier nützlich, da Postgres BRIN-Indizes unterstützt? Ich plane, ungefähr 140 Millionen Zeilen in Daten auf Postgres zu speichern. Ist BRIN der richtige Index für eine so große Tabelle?
Arya

2

Wenn Sie den Index aktivieren (prop_id, "timestamp", "value"), kann er einen Nur-Index-Scan verwenden, um den Wert zu berechnen, ohne jemals die Tabelle zu besuchen. Dies könnte viel zufälligen Festplattenzugriff sparen.

Um den größtmöglichen Nutzen zu erzielen, müssen Sie beim Staubsaugen des Tisches aggressiv sein. Die Standardeinstellungen für Autovac sind nicht aggressiv genug für Nur-Einfügen-Tabellen, für die Sie nur Index-Scans effizient unterstützen möchten.


Das Hinzufügen des Werts könnte in der Tat interessant sein. Ich werde prüfen, ob dies die Dinge beschleunigt. Haben Sie Vorschläge für Vakuumeinstellungen oder Dokumentationen, die ich mir ansehen kann?
Exelian
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.