In PostgreSQL 9.3 versuche ich, einen effizienten Index für eine selten verwendete boolesche Spalte (0,00001% aller Datensätze) zu erstellen. Zu diesem Zweck habe ich diesen Beitrag auf SO entdeckt: https://stackoverflow.com/a/12026593/808921
Ich versuche, die von Erwin Brandstetter empfohlene Funktion "Teilindex" von PostgreSQL zu nutzen. Ich habe bereits eine Tabelle mit ein paar Millionen Datensätzen und möchte den Index wie folgt zu dieser Tabelle hinzufügen:
CREATE INDEX schema_defs_deprovision ON schema_defs (deprovision)
WHERE deprovision = 0;
(Die überwiegende Mehrheit der Aufzeichnungen wird haben deprovision = 1
)
Das Problem ist, dass PostgreSQL, wenn ich versuche, diesen Index mit den einfachsten Abfragen zu verwenden, so tut, als ob er nicht vorhanden wäre:
explain select * from schema_defs where deprovision = 0;
Seq Scan on schema_defs (cost=0.00..1.05 rows=1 width=278)
Filter: (deprovision = 0)
Das wirklich Seltsame ist, dass ich festgestellt habe, dass wenn dieser Index erstellt wird, bevor Daten in der Tabelle vorhanden sind, er tatsächlich einwandfrei funktioniert. Glaubst du mir nicht? Hier sind einige SQL Fiddle-Einträge, die dies beweisen:
Teilindex nach Einfügungen erstellt (Index funktioniert nicht)
Teilindex vor Einfügungen erstellt (Index funktioniert ordnungsgemäß)
Erweitern Sie in beiden Fällen einfach den Link "Ausführungsplan anzeigen", um zu sehen, wovon ich spreche.
Meine Frage lautet also: Was muss ich tun, damit PostgreSQL einen Teilindex für eine Tabelle verwendet, in der sich Daten befanden, bevor der Index erstellt wurde?
Übrigens bin ich auch der Entwickler von SQL Fiddle und diese Frage hängt mit einer neuen Entwicklungsanstrengung zusammen, die ich dafür mache.
quelle
Antworten:
Ausführen,
ANALYZE
nachdem der Index hinzugefügt wurde. Und stellen Sie sicher , dass die Spaltedeprovision
hat Statistiken. Wie überprüfe ich?Grundlegende Statistiken in
pg_class
:Datenhistogramme pro Spalte in
pg_stats
(pg_statistics
):Das Handbuch:
In Ihrem Fall würde die Analyse nur einer Spalte die Aufgabe erfüllen:
Wenn Sie gerade dabei sind, macht es keinen Sinn, den Index für die Spalte zu haben
deprovision
. In Anbetracht des Prädikats enthältWHERE deprovision = 0
es keine zusätzlichen Informationen. Sie können auch einen konstanten Ausdruck verwenden:Nur ein Proof of Concept. Dies wäre nicht nützlicher. In diesem speziellen Fall würden Sie einen Index nicht benötigen Spalte überhaupt, aber Sie müssen mindestens eine Spalte oder einen Ausdruck zur Verfügung stellen. Verwenden Sie daher den Primärschlüssel (da er sich nicht ändert und ohnehin indiziert ist, führen Sie keine weiteren Einschränkungen / Gemeinkosten ein) oder eine andere kleine Spalte (<= 8 Byte), die für Abfragen nützlich sein kann.
sqlfiddle.com
Die Demo-Geigen sind irreführend .
Ihre Demo-Tabelle hat nur 4 Zeilen. Postgres sollte nicht mit dem Index sein. Ein ähnliches Problem, genau umgekehrt. Postgres verfügt nicht unmittelbar nach dem Erstellen über Statistiken in der Tabelle - bis zum ersten Durchlauf von
ANALYZE
. Dann weiß es , dass es nur 4 Zeilen gibt und den Index nicht mehr berührt.Warum funktioniert es bei Ihrer zweiten Demo richtig? Das Handbuch:
Meine kühne Betonung. Wenn Sie den Index nach dem Einfügen von Zeilen erstellen , werden diese grundlegenden Statistiken
pg_class
aktualisiert. Aber nur diese, nicht die detaillierten Statistiken inpg_statistic
:Damit Postgres den Teilindex verwendet (insbesondere in seiner ursprünglichen Form, die für nichts anderes nützlich ist), benötigen Sie auch das Datenhistogramm
pg_statistic
, um den Abfrageplaner darüber zu informieren, dass diesdeprovision = 0
tatsächlich ein seltener Fall ist. Daher lohnt es sich, den Index zu verwenden.Dafür sorgt Autovacuum . Es plant
VACUUM
undANALYZE
automatisch. Zwischen den Schreibvorgängen in die Tabelle und dem nächstenANALYZE
Durchlauf liegt jedoch ein Zeitrahmen (abhängig von den Einstellungen und der Auslastung) . Wenn Sie Abfragen unmittelbar nach der Tabellenerstellung oder Änderungen an der Tabelle ausführen, werden diese letzten Änderungen noch nicht in der Statistik berücksichtigt. Es ist egal, ob dies die Statistik nicht in relevanter Weise ändert. Wenn dies beispielsweise nach einer großenINSERT
oder unmittelbar nach der Tabellenerstellung der Fall ist , führen Sie dieseANALYZE
manuell aus, um die richtigen Abfragepläne zu erhalten.Beachten Sie, dass temporäre Tabellen überhaupt nicht von Autovakuum abgedeckt werden. Sie müssen diese immer
ANALYZE
manuell ausführen , wenn Sie sie benötigen:Ich weiß nicht, wie Sie das Autovakuum konfiguriert haben und ob / wann Sie es
ANALYZE
manuell ausführen . Aber ich habe in der Vergangenheit festgestellt, dass sqlfiddle aufgrund fehlender / veralteter Statistiken irreführend sein kann.Mich würde sehr interessieren, wie
ANALYZE
hinter den Vorhängen auf sqlfiddle gehandhabt wird. Es ist vielleicht am besten, nichts Besonderes zu tun, aber einige Informationen wären willkommen. Vielleicht eine einfache Webseite pro verfügbarer RDBMS-Version?Demo
Ich habe eine SQL-Geige erstellt , um die Auswirkungen
CREATE INDEX
undANALYZE
auf die verschiedenen Statistiken zu demonstrieren .Die Effekte zeigen sich (zumindest) beim ersten Lauf für mich. Bei späteren Läufen möglicherweise nicht reproduzierbar. Sie müssten ein neues Schema erstellen und erneut ausführen.
Zuerst sehen wir keine grundlegenden Statistiken in
pg_class
:Noch irgendwelche Einträge für
deprovision
inpg_statistics
überhaupt (kein Ergebnis).Postgres hat keine Ahnung, was in der Tabelle steht, und verwendet standardmäßig den Index - was eine schlechte Wahl ist!
Danach sehen
CREATE INDEX
wir grundlegende Statistiken, aber immer noch kein Datenhistogramm inpg_statistics
.Nachdem
ANALYZE
wir beide gesehen haben.Bei korrekter Statistik verwendet Postgres jetzt einen sequentiellen Scan (gute Wahl, selbst wenn ein Index vorhanden ist - dies wäre für so wenige Zeilen teurer).
quelle