Ich habe eine Tabelle mit ungefähr 100 Millionen Zeilen. Es werden nur einmal pro Tag Daten eingefügt, aber wir müssen select
viel tun . Die select
s sind normalerweise einfach, müssen aber manchmal Hunderttausende von Zeilen zurückgeben.
Es ist einzigartig basiert auf drei Säulen node_id
, pricedate
, hour
die integer sind, Zeitstempel, integer sind. Es war für die meisten Abfragen langsam, aber ich habe es gruppiert node_id
, pricedate
und 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_dup
mit 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 select
von der duplizierten Tabelle, der effizient ist?
Ich verwende PostgreSQL 9.4 zu Hause und 9.5 bei Google.
quelle
Antworten:
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.
quelle
Sie können eine materialisierte Ansicht für Ihre Tabelle erstellen :
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):Ihre Kopie ist also da. Wenn Sie es gruppieren möchten, benötigen Sie einen Index dafür:
Wenn Sie es dann benötigen (im Grunde genommen nach dem täglichen Laden der Daten), führen Sie a aus
Ändern Sie dann den zweiten Abfragetyp, um anstelle der Tabelle zum MV zu wechseln.
quelle
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?
quelle