Ist es möglich, zwei Kopien einer Tabelle nahtlos in unterschiedlichen Anordnungen zu gruppieren?

7

Ich habe eine Tabelle mit ungefähr 100 Millionen Zeilen. Es werden nur einmal pro Tag Daten eingefügt, aber wir müssen selectviel tun . Die selects sind normalerweise einfach, müssen aber manchmal Hunderttausende von Zeilen zurückgeben.

Es ist einzigartig basiert auf drei Säulen node_id, pricedate, hourdie integer sind, Zeitstempel, integer sind. Es war für die meisten Abfragen langsam, aber ich habe es gruppiert node_id, pricedateund das hat die Langsamkeit für die meisten Abfragen behoben. Diese Abfragen waren vom Typ:

select * from mytable where node_id in (1,2,3,4)

Gelegentlich müssen wir noch Fragen stellen wie:

select * from mytable where pricedate>='2016-05-01'

Diese sind immer noch langsam, da sie zuerst geclustert werden node_id. Wir haben bereits einen Index pricedate. Das Problem ist, dass die Benutzer häufig genügend Daten benötigen, damit die Abfrage-Engine den Index auslöst und einen seq-Scan verwendet. Sobald ein seq-Scan verwendet wird, profitiert es erheblich davon, dass die Daten so geclustert werden, wie sie abgefragt werden. Dies führt zu dem Problem, bei dem einige Abfragen von einem Clustering und andere von dem anderen profitieren:

Es wäre schön, wenn es eine Möglichkeit gäbe, zwei physische Kopien der Tabelle zu erstellen, bei denen eine Kopie auf die eine und die andere auf eine andere Weise gruppiert wird, der Benutzerzugriff darauf jedoch so aussieht, als gäbe es nur eine Tabelle, und die DB-Engine würde dies sicherstellen sind synchron. Natürlich würde dies Schreibstrafen nach sich ziehen, aber das ist für unsere Verwendung unerheblich.

Wäre so etwas möglich?

Ich vermute, es gibt keine eingebaute Möglichkeit, das zu tun, was ich beschreibe. Um es trotzdem zu tun, würde ich wahrscheinlich eine Tabelle erstellen, die mytable_dupmit der gleichen eindeutigen Schlüsselbeschränkung aufgerufen wird , aber mit dem alternativen Clustering, und dann Trigger einrichten, um sie einzufügen, wenn der Master eingefügt / aktualisiert / gelöscht wird. Das scheint machbar, aber von hier aus gibt es einen vernünftigen Weg selectvon der duplizierten Tabelle, der effizient ist?

Ich verwende PostgreSQL 9.4 zu Hause und 9.5 bei Google.

Dean MacGregor
quelle
Ich würde einen BRIN-Index auf (Prädikat) versuchen.
Ypercubeᵀᴹ

Antworten:

5

Um die Daten in zwei verschiedenen physischen Sequenzen zu halten, müssen die Daten zweimal gespeichert werden. Dies kann erreicht werden, indem ein zweiter Index definiert wird. Ein Deckungsindex enthält alle Spalten, die für eine Abfrage erforderlich sind. Auf diese Weise muss der Optimierer nicht auf die Basistabelle verweisen, um weitere Werte zu lesen, und es ist unwahrscheinlich, dass er zu einem Basistabellenscan für den Abfrageplan zurückkehrt. Der Optimierer führt einen Nur-Index-Scan durch . Da die Auswahl des Index vom Optimierer und nicht vom Programmierer getroffen wird, muss kein Anwendungscode geändert werden, um die Vorteile beim Lesen zu nutzen. Es sind keine weiteren Objekte erforderlich, um die Konsistenz beim Schreiben aufrechtzuerhalten.

Die in der WHERE-Klausel verwendeten Spalten sind die führenden Spalten des Index. Die Reihenfolge der anderen Spalten ist unwichtig. Wenn PostgreSQL die INCLUDE-Syntax unterstützt, kann dieser Index geändert werden, um ihn zu verwenden.

Zu den Nachteilen gehören a) zusätzliche Festplatte zum Speichern dieser Daten b) zusätzliche Latenz während des Schreibens, um den Index aufrechtzuerhalten c) mehr Systemwartung für Reorgs und dergleichen erforderlich, und d) wenn sich Abfragen ändern, müssen sich die abdeckenden Indizes ändern, um mit e übereinzustimmen ) entsprechend größere und längere Backups und Wiederherstellungen.

Michael Green
quelle
Es gibt Chancen, dass dies eine bessere Antwort ist als meine :)
Dezso
3

Sie können eine materialisierte Ansicht für Ihre Tabelle erstellen :

CREATE MATERIALIZED VIEW thecopy AS SELECT * FROM mytable;

Fügen Sie dann einen eindeutigen Index hinzu, der mit Ihrer PK übereinstimmt mytable(Sie können dort keine "echte" PK hinzufügen, da es sich nicht um eine "echte" Tabelle handelt):

CREATE UNIQUE INDEX ON thecopy (node_id, pricedate, hour);

Ihre Kopie ist also da. Wenn Sie es gruppieren möchten, benötigen Sie einen Index dafür:

CREATE INDEX ON thecopy (pricedate);
CLUSTER thecopy USING thecopy_pricedate_idx;
-- You can also do 
-- ALTER MATERIALIZED VIEW thecopy CLUSTER ON thecopy_pricedate_idx;
-- https://www.postgresql.org/docs/current/static/sql-altermaterializedview.html

Wenn Sie es dann benötigen (im Grunde genommen nach dem täglichen Laden der Daten), führen Sie a aus

REFRESH MATERIALIZED VIEW [CONCURRENTLY] thecopy;

Ändern Sie dann den zweiten Abfragetyp, um anstelle der Tabelle zum MV zu wechseln.

dezso
quelle
Ich weiß, dass eine materialisierte Ansicht rationalisierter ist als eine Tabelle mit Triggern, aber mein Hauptziel ist es, den Auswahlprozess rationalisiert zu gestalten.
Dean MacGregor
@ DeanMacGregor Nun, ohne eine separate Tabelle haben Sie keine separate physische Reihenfolge (was Ihnen anscheinend hilft). Wenn Sie mit Rationalisierung meinen, dass Sie irgendwo auswählen möchten, wo dann eine der beiden Kopien versandt wird, habe ich keine Ahnung.
Dekso
-1

Angenommen, Sie möchten es für die Benutzer der select-Klausel einfach halten und wissen nicht, wie sie sie ausführen ...

Wie wäre es mit einer Funktion?

Eine Alternative:

Übergeben Sie die gesamte select-Klausel als Parameter an eine Funktion.

Analysieren Sie es für die Spalte where-Klausel

und dann auf die Tabelle oder materialisierte Ansicht lenken, wie von @dezso vorgeschlagen?

Amacvar
quelle