it-swarm.com.de

Verbesserte Abfragegeschwindigkeit: einfaches SELECT in großen Postgres-Tabellen

Ich habe Probleme mit der Geschwindigkeit in einer SELECT-Abfrage in einer Postgres-Datenbank.

Ich habe eine Tabelle mit zwei ganzzahligen Spalten als Schlüssel: (int1, int2) Diese Tabelle hat ungefähr 70 Millionen Zeilen.

Ich muss zwei einfache SELECT-Abfragen in dieser Umgebung erstellen:

SELECT * FROM table WHERE int1=X;
SELECT * FROM table WHERE int2=X;

Diese beiden Auswahlen liefern jeweils rund 10.000 Reihen von diesen 70 Millionen. Damit dies so schnell wie möglich funktioniert, habe ich mir überlegt, zwei HASH-Indizes zu verwenden, einen für jede Spalte. Leider sind die Ergebnisse nicht so gut:

                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on lec_sim  (cost=232.21..25054.38 rows=6565 width=36) (actual time=14.759..23339.545 rows=7871 loops=1)
   Recheck Cond: (lec2_id = 11782)
   ->  Bitmap Index Scan on lec_sim_lec2_hash_ind  (cost=0.00..230.56 rows=6565 width=0) (actual time=13.495..13.495 rows=7871 loops=1)
         Index Cond: (lec2_id = 11782)
 Total runtime: 23342.534 ms
(5 rows)

Dies ist ein EXPLAIN ANALYZE-Beispiel für eine dieser Abfragen. Es dauert ungefähr 23 Sekunden. Ich gehe davon aus, diese Informationen in weniger als einer Sekunde zu erhalten.

Dies sind einige Parameter der postgres db config:

work_mem = 128MB
shared_buffers = 2GB
maintenance_work_mem = 512MB
fsync = off
synchronous_commit = off
effective_cache_size = 4GB

Jede Hilfe, Kommentar oder Gedanke wäre wirklich dankbar.

Danke im Voraus.

24
alexdemartos

Meine Kommentare in eine Antwort extrahieren: Die Indexsuche hier war sehr schnell - die ganze Zeit wurde mit dem Abrufen der eigentlichen Zeilen verbracht. 23 Sekunden/7871 Zeilen = 2,9 Millisekunden pro Zeile, was zum Abrufen von Daten im Disk-Subsystem sinnvoll ist. Sucht ist langsam; Sie können a) Ihren Datensatz in den RAM-Speicher einpassen, b) SSDs kaufen oder c) Ihre Daten vorab organisieren, um Suchanfragen zu minimieren.

PostgreSQL 9.2 verfügt über eine Funktion namens index-only-Scans , mit der Abfragen (normalerweise) ohne Zugriff auf die Tabelle beantwortet werden können. Sie können dies mit der Indexeigenschaft btree kombinieren, indem Sie die Reihenfolge automatisch beibehalten, um diese Abfrage schnell zu machen. Sie erwähnen int1, int2 und zwei Floats:

CREATE INDEX sometable_int1_floats_key ON sometable (int1, float1, float2);
CREATE INDEX sometable_int2_floats_key ON sometable (int2, float1, float2);

SELECT float1,float2 FROM sometable WHERE int1=<value>; -- uses int1 index
SELECT float1,float2 FROM sometable WHERE int2=<value>; -- uses int2 index

Beachten Sie auch, dass die Suchvorgänge der Festplatte dadurch nicht auf magische Weise gelöscht werden. Sie werden lediglich von der Abfragezeit zur Einfügezeit verschoben. Es kostet Sie auch Speicherplatz, da Sie die Daten duplizieren. Dies ist wahrscheinlich der Kompromiss, den Sie wollen.

31
willglynn

Vielen Dank, Willglyn. Wie Sie bemerkt haben, war das Problem das Suchen durch die HD und nicht das Suchen nach den Indizes. Sie haben viele Lösungen vorgeschlagen, z. B. das Laden des Datensatzes in RAM oder den Kauf einer SSDs HD. Wenn Sie jedoch die beiden Dinge vergessen, die das Verwalten von Dingen außerhalb der Datenbank selbst betreffen, haben Sie zwei Vorschläge gemacht:

  1. Reorganisieren Sie die Daten, um die Suche nach den Daten zu reduzieren.
  2. Verwenden der PostgreSQL 9.2-Funktion "Nur Index-Scans"

Da ich unter einem PostgreSQL 9.1 Server bin, habe ich mich für die Option "1" entschieden.

Ich habe eine Kopie des Tisches gemacht. Ich habe also jetzt dieselbe Tabelle zweimal mit den gleichen Daten. Ich habe für jeden einen Index erstellt, wobei der erste Index mit (int1) und der zweite mit (int2) indexiert wurde. Dann habe ich beide (CLUSTER-Tabelle USING ind_intX) nach ihren jeweiligen Indizes gruppiert.

Ich poste jetzt eine EXPLAIN-ANALYSE derselben Abfrage, die in einer dieser Clustertabellen ausgeführt wurde:

 QUERY PLAN 
----------------------------------------- -------------------------------------------------- ----------------------------------------------  
Index Scannen Sie mit lec_sim_lec2id_ind auf lec_sim_lec2id (Kosten = 0.00..21626.82 Zeilen = 6604 Breite = 36) (tatsächliche Zeit = 0.051..1.500 Zeilen = 8119 Schleifen = 1) 
 Index Cond: (Lec2_id = 12300) Gesamtlaufzeit: 
1.822 ms (3 Zeilen) 

Nun ist das Suchen sehr schnell. Ich ging von 23 Sekunden auf ~ 2 Millisekunden zurück, was eine beeindruckende Verbesserung darstellt. Ich denke, dieses Problem ist für mich gelöst, ich hoffe, dass dies auch für andere nützlich sein kann, die das gleiche Problem haben.

Vielen Dank, Willglynn.

20
alexdemartos

Ich hatte einen Fall von extrem langsamen Abfragen, bei denen zwischen einer Tabelle mit 33 Millionen Zeilen und einer untergeordneten Tabelle mit 2,4 Milliarden Zeilen einfache ein-zu-viele-Joins (in PG v9.1) ausgeführt wurden. Ich habe einen CLUSTER für den Fremdschlüsselindex für die untergeordnete Tabelle ausgeführt, stellte jedoch fest, dass dies mein Problem mit Abfragezeitlimits nicht gelöst hat, selbst für die einfachsten Abfragen. Das Ausführen von ANALYZE löste das Problem ebenfalls nicht.

Was einen großen Unterschied machte, war die Durchführung eines manuellen VACUUMs sowohl am übergeordneten Tisch als auch am untergeordneten Tisch. Selbst als der übergeordnete Tisch seinen VACUUM-Prozess abgeschlossen hatte, stieg ich von 10-Minuten-Zeitüberschreitungen zu Ergebnissen, die in einer Sekunde zurückkehrten.

Was ich davon nehme, ist, dass regelmäßige VACUUM-Operationen selbst für Version 9.1 noch kritisch sind. Der Grund, warum ich dies tat, war, dass ich bemerkte, dass Autovakuum seit mindestens zwei Wochen an keinem der Tische lief, und seitdem gab es viele Upserts und Inserts. Es kann sein, dass ich den Autovakuumauslöser verbessern muss, um dieses Problem in Zukunft zu lösen, aber ich kann sagen, dass eine 640 GB-Tabelle mit ein paar Milliarden Zeilen gut funktioniert, wenn alles aufgeräumt wird. Ich musste den Tisch noch nicht partitionieren, um eine gute Leistung zu erzielen.

3
Robert Casey

Wenn Sie auf Ihrem Postgres-Rechner einen schnellen Solid-State-Speicher haben, versuchen Sie Folgendes:

random_page_cost=1.0

In Ihrem in Ihrem postgresql.conf

Der Standardwert ist random_page_cost=4.0. Dies ist für die Speicherung mit hohen Suchzeiten wie alten sich drehenden Festplatten optimiert. Dies ändert die Kostenberechnung für das Suchen und hängt weniger von Ihrem Gedächtnis ab. 

Allein diese Einstellung verbesserte meine Filterabfrage von 8 Sekunden auf 2 Sekunden an einem langen Tisch mit ein paar Millionen Datensätzen. 

Die andere wesentliche Verbesserung ergab sich durch das Erstellen von Indizes mit allen Booleen-Spalten in meiner Tabelle. Dadurch wurde die Abfrage von 2 Sekunden auf etwa 1 Sekunde reduziert. Überprüfen Sie @ willglynns Antwort darauf. 

Hoffe das hilft!

0
Nick Woodhams