Nehmen wir ein paar Annahmen an:
Ich habe einen Tisch, der so aussieht:
a | b
---+---
a | -1
a | 17
...
a | 21
c | 17
c | -3
...
c | 22
Fakten zu meinem Set:
Die Größe der gesamten Tabelle beträgt ~ 10 10 Zeilen.
Ich habe ~ 100k Zeilen mit einem Wert
a
in der Spaltea
, ähnlich wie bei anderen Werten (zBc
).Das bedeutet ~ 100k verschiedene Werte in der Spalte 'a'.
Die meisten meiner Abfragen lesen alle oder die meisten Werte für einen bestimmten Wert in einem, z
select sum(b) from t where a = 'c'
.Die Tabelle ist so geschrieben, dass aufeinanderfolgende Werte physisch nahe beieinander liegen (entweder ist sie in der richtigen Reihenfolge geschrieben, oder wir gehen davon aus
CLUSTER
, dass sie für diese Tabelle und Spalte verwendet wurdea
).Die Tabelle wird selten, wenn überhaupt, aktualisiert. Wir sorgen uns nur um die Lesegeschwindigkeit.
Die Tabelle ist relativ eng (z. B. ~ 25 Bytes pro Tupel, + 23 Bytes Overhead).
Die Frage ist nun, welche Art von Index soll ich verwenden? Mein Verständnis ist:
BTree Mein Problem hierbei ist, dass der BTree-Index sehr groß sein wird, da meines Wissens doppelte Werte gespeichert werden (dies muss geschehen, da nicht angenommen werden kann, dass die Tabelle physisch sortiert ist). Wenn der BTree sehr groß ist, muss ich am Ende sowohl den Index als auch die Teile der Tabelle lesen, auf die der Index verweist. (Wir können benutzen
fillfactor = 100
den Index etwas verkleinern.)BRIN Ich verstehe, dass ich hier einen kleinen Index haben kann, auf Kosten des Lesens nutzloser Seiten. Ein kleiner
pages_per_range
Index bedeutet, dass der Index größer ist (was bei BRIN ein Problem ist, da ich den gesamten Index lesen muss), und ein großer Indexpages_per_range
bedeutet, dass ich viele nutzlose Seiten lesen werde. Gibt es eine Zauberformel, um einen guten Wert von zu finden?pages_per_range
, der diese Kompromisse berücksichtigt?GIN / GiST Nicht sicher, ob diese hier relevant sind, da sie hauptsächlich für die Volltextsuche verwendet werden, aber ich höre auch, dass sie gut mit doppelten Schlüsseln umgehen können. Wäre hier entweder ein
GIN
oder einGiST
Index hilfreich?
Eine andere Frage ist, ob Postgres die Tatsache verwendet, dass eine Tabelle CLUSTER
im Abfrageplaner bearbeitet wird (vorausgesetzt, es werden keine Aktualisierungen vorgenommen) (z. B. durch binäre Suche nach den relevanten Start- / End-Seiten). Kann ich in gewisser Hinsicht einfach alle meine Spalten in einem BTree speichern und die Tabelle insgesamt löschen (oder etwas Äquivalentes erreichen, ich glaube, das sind Clustered-Indizes in SQL Server)? Gibt es einen hybriden BTree / BRIN-Index, der hier helfen würde?
Ich würde es lieber vermeiden, Arrays zum Speichern meiner Werte zu verwenden, da meine Abfrage auf diese Weise weniger lesbar wird (ich verstehe, dass dies die Kosten für den Overhead von 23 Bytes pro Tupel verringern würde, indem die Anzahl der Tupel verringert wird).
Antworten:
Nicht unbedingt - Mit einem Btree-Index, der „abdeckt“, erzielen Sie die schnellste Lesezeit, und wenn Sie nur das möchten (dh wenn Sie sich den zusätzlichen Speicher leisten können), ist dies die beste Wahl.
Wenn Sie sich den Speicheraufwand für einen abdeckenden Btree-Index nicht leisten können, ist BRIN ideal für Sie, da bereits Clustering vorhanden ist (dies ist für BRIN von entscheidender Bedeutung). BRIN-Indizes sind winzig , sodass sich wahrscheinlich alle Seiten im Arbeitsspeicher befinden, wenn Sie einen geeigneten Wert für auswählen
pages_per_range
.Keine Zauberformel, aber beginnen Sie mit
pages_per_range
etwas weniger als der Durchschnittsgröße (in Seiten), die der Durchschnittswerta
einnimmt. Sie versuchen wahrscheinlich, Folgendes zu minimieren: (Anzahl der gescannten BRIN-Seiten) + (Anzahl der gescannten Heap-Seiten) für eine typische Abfrage. Suchen SieHeap Blocks: lossy=n
im Ausführungsplan nachpages_per_range=1
und vergleichen Sie sie mit anderen Werten fürpages_per_range
- dh sehen Sie, wie viele unnötige Heap-Blöcke gescannt werden.GIN ist vielleicht eine Überlegung wert, aber wahrscheinlich nicht GiST. Wenn das natürliche Clustering jedoch wirklich gut ist, ist BRIN wahrscheinlich die bessere Wahl.
Hier ist ein Beispielvergleich zwischen den verschiedenen Indextypen für Dummy-Daten, ähnlich wie bei Ihnen:
Tabelle und Indizes:
Beziehungsgrößen:
btree abdecken:
einfacher Baum:
BRIN pages_per_range = 4:
BRIN pages_per_range = 2:
GIN:
dbfiddle hier
quelle
Bitmap Index Scan
, dass es "den gesamten Index lesen" bedeutet, aber vielleicht ist das die falsche Lesart. OracleCOMPRESS
sieht nach etwas aus, das hier nützlich wäre, da es die Größe des B-Baums verringern würde, aber ich bleibe bei pg!Neben btree und brin die scheinen , die am sinnvollsten Möglichkeiten, einige andere, exotische Optionen , die eine Untersuchung wert sein könnte - sie könnten nützlich oder nicht in Ihrem Fall:
INCLUDE
Indizes . Sie werden - hoffentlich - irgendwann im September 2017 in der nächsten Hauptversion (10) von Postgres erscheinen. Ein Index auf(a) INCLUDE (b)
hat dieselbe Struktur wie ein Index auf(a)
, enthält jedoch in den Blattseiten alle Werte vonb
(aber ungeordnet). Was bedeutet, dass Sie es nicht zum Beispiel für verwenden könnenSELECT * FROM t WHERE a = 'a' AND b = 2 ;
. Der Index kann verwendet werden, aber während ein(a,b)
Index die übereinstimmenden Zeilen bei einer einzelnen Suche findet, muss der Include-Index die (möglicherweise 100 KB wie in Ihrem Fall) Werte durchlaufen, die übereinstimmen,a = 'a'
und dieb
Werte prüfen .Auf der anderen Seite ist der Index etwas weniger breit als der
(a,b)
Index, und Sie müssen die Reihenfolge nicht einhalten,b
damit Ihre Abfrage berechnet werden kannSUM(b)
. Sie könnten zum Beispiel auch haben(a) INCLUDE (b,c,d)
Dies kann für ähnliche Abfragen wie Ihre verwendet werden, die in allen drei Spalten zusammengefasst sind.Gefilterte (Teil-) Indizes . Ein Hinweis , dass vielleicht ein bisschen verrückt klingt * zuerst:
Ein Index für jeden
a
Wert. In Ihrem Fall um 100K Indizes. Bedenken Sie, dass jeder Index sehr klein ist, sowohl in Bezug auf die Größe (Anzahl der Zeilen) als auch in Bezug auf die Breite (da nurb
Werte gespeichert werden). In allen anderen Aspekten fungiert es (die 100K-Indizes zusammen) als B-Tree-Index,(a,b)
während der Platz eines(b)
Index verwendet wird.Nachteil ist, dass Sie sie jedes Mal selbst erstellen und pflegen müssen, wenn ein neuer Wert von
a
in die Tabelle eingefügt wird. Da Ihre Tabelle ziemlich stabil ist, ohne viele (oder keine) Einfügungen / Aktualisierungen, scheint dies kein Problem zu sein.Übersichtstabellen. Da die Tabelle relativ stabil ist, können Sie jederzeit eine Übersichtstabelle mit den am häufigsten benötigten Aggregaten (
sum(b), sum(c), sum(d), avg(b), count(distinct b)
usw.) erstellen und auffüllen. Es ist klein (nur 100 KB Zeilen) und muss nur einmal ausgefüllt und aktualisiert werden, wenn Zeilen in der Haupttabelle eingefügt / aktualisiert / gelöscht werden.*: Idee kopiert von diesem Unternehmen, das 10 Millionen Indizes in seinem Produktionssystem ausführt: The Heap: 10 Millionen Postgresql-Indizes in der Produktion ausführen (und zählen) .
quelle
SUM
als Beispiel genommen, aber in der Praxis können meine Abfragen nicht vorberechnet werden (sieselect ... from t where a = '?' and ??
ähneln??
eher einer anderen benutzerdefinierten Bedingung.??
ist;)DO
in dieser verwandten Antwort .