diplomarbeit

Werbung
T ECHNISCHE U NIVERSITÄT I LMENAU
Fakultät für Informatik und Automatisierung
Institut für Praktische Informatik und Medieninformatik
Fachgebiet Datenbanken und Informationssysteme
D IPLOMARBEIT
Anfragengetriebenes Index-Tuning in PostgreSQL
Inventarisierungsnummer 2004-09-30/101/IN99/2254
eingereicht im:
von:
Betreuer:
Septemer 2004
Mario Stiffel
geboren am 25. Februar 1979
in Eisenach
Prof. Sattler
2
Erklärung
Ich versichere, dass ich die vorliegende Diplomarbeit selbständig verfasst und keine
anderen als die angegebenen Quellen und Hilfsmittel benutzt habe.
Die Arbeit hat in gleicher oder ähnlicher Form noch keinem Prüfungsamt vorgelegen.
Ilmenau, den 13. September 2004
............................................
2004-09-30/101/IN99/2254
INHALTSVERZEICHNIS
3
Inhaltsverzeichnis
Thesen
5
1
Einleitung
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Ziel der Diplomarbeit . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Index-Set Bestimmung . . . . . . . . . . . . . . . . . . . . . . . . .
6
6
8
8
2
Existierende Systeme
2.1 Oracle Virtual Index Wizard . . . . . . . .
2.2 Microsoft SQL Server Index Selection Tool
2.3 IBM DB2 Advisor . . . . . . . . . . . . .
2.4 QUIET . . . . . . . . . . . . . . . . . . .
2.5 Zusammenfassung . . . . . . . . . . . . .
3
PostgreSQL
3.1 Indizes . . . . . . . . . . . . . . . .
3.1.1 Indizes allgemein . . . . . .
3.1.2 Index-Scan . . . . . . . . .
3.1.3 Index-Typen in PostgreSQL
3.2 Anfrageplanung . . . . . . . . . . .
3.3 TPC-H Datenbank . . . . . . . . .
3.4 Der Weg einer Anfrage . . . . . . .
3.5 List-Package . . . . . . . . . . . .
3.6 Trees . . . . . . . . . . . . . . . . .
3.6.1 Nodes . . . . . . . . . . . .
3.6.2 Query-Tree . . . . . . . . .
3.6.3 Plan-Tree . . . . . . . . . .
3.7 CommandCounter . . . . . . . . . .
3.8 Disk-Pages . . . . . . . . . . . . .
3.9 System Catalog . . . . . . . . . . .
3.9.1 Access Methods . . . . . .
3.9.2 Operatoren . . . . . . . . .
3.9.3 Operator Class . . . . . . .
2004-09-30/101/IN99/2254
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
10
10
12
14
16
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
18
18
21
21
22
23
25
27
29
29
33
41
46
48
50
50
51
52
INHALTSVERZEICHNIS
4
5
4
3.10 Run-time Configuration . . . . . . . . . . . . . . . . . . . . . . . . .
53
Index-Advisor
4.1 Grundlegender Ablauf . . . . . . . . . .
4.2 Index-Kandidaten-Liste . . . . . . . . . .
4.3 Regelwerk . . . . . . . . . . . . . . . . .
4.4 Virtuelle Indizes . . . . . . . . . . . . . .
4.5 Systemtabelle pg_indexadvisor . . . . . .
4.6 Ablauf . . . . . . . . . . . . . . . . . . .
4.6.1 Input . . . . . . . . . . . . . . .
4.6.2 Ausführung ohne virtuelle Indizes
4.6.3 Index-Kandidaten . . . . . . . . .
4.6.4 Anlegen der virtuellen Indizes . .
4.6.5 Ausführung mit virtuellen Indizes
4.6.6 Entfernen der virtuellen Indizes .
4.6.7 Speicherung des Ergebnisses . . .
4.7 Implementierung . . . . . . . . . . . . .
4.8 Advisor-Configuration . . . . . . . . . .
4.9 Auswertung . . . . . . . . . . . . . . . .
4.10 Einsatz . . . . . . . . . . . . . . . . . . .
4.11 Fazit . . . . . . . . . . . . . . . . . . . .
57
58
59
61
63
68
69
70
71
71
73
73
74
75
76
77
78
82
82
Ausblick
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
84
Abbildungsverzeichnis
86
Literaturverzeichnis
87
2004-09-30/101/IN99/2254
INHALTSVERZEICHNIS
5
Thesen
• Eines der wichtigsten Qualitätsmerkmale eines Datenbanksystems ist die Performance.
• Mittels Indizes kann die benötigte Zeit zur Verarbeitung von Anfragen drastisch
reduziert werden.
• Die Struktur einer Datenbank, ihre Inhalte und deren Nutzung ändert sich im
Laufe der Zeit. Dies macht eine kontinuierliche Anpassung der Indizes notwendig.
• Große Datenbanken können nicht manuell optimiert werden. Ein Beispiel dafür
ist die Datenbank der Software „SAP R/3”.
• Die Ermittlung eines Index-Set für einen gegebenen Workload hat einen exponentiellen Zeitbedarf.
• Die Nutzenabschätzung eines Index ist mit virtuellen Indizes möglich. Sie liefern mit geringem Aufwand eine gute Schätzung der Realität.
• PostgreSQL ist eine gute Basis für den Index-Advisor. Das Datenbanksystem
ermöglicht die vollständige Integration des Advisors durch Open-Source.
• Der Index-Advisor benötigt drei wesentliche Schritte zur Ermittlung der IndexKandidaten. Der erste ist die Anfrageplanung ohne virtuelle Indizes. Der zweite
ist die Anfragenplanung mit virtuellen Indizes und dritter ist der Vergleich der
Ergebnisse.
• Die Empfehlungen des Index-Advisors sind gut. Sie stimmen mit der Verwendung von realen Indizes überein.
• Der zusätzliche Overhead des Index-Advisors ist minimal und wird durch seinen
Mehrwert gerechtfertigt.
Ilmenau, den 13. September 2004
............................................
2004-09-30/101/IN99/2254
KAPITEL 1. EINLEITUNG
6
Kapitel 1
Einleitung
1.1
Motivation
Indizes sind neben Parametern eines der wichtigsten Mittel zur Optimierung einer Datenbank. Mithilfe von Indizes können Anfragen an ein Datenbanksystem effizienter
abgearbeitet werden. Dies ermöglicht es, mit einem geringeren Hardwarebedarf das
gleiche Ergebnis zu erzielen oder bei gleicher Hardware mehr Anfragen zu bearbeiten.
Des Weiteren sinkt die Reaktionszeit der Datenbank, was in vielen Fällen ein unschätzbarer Vorteil sein kann. Die Vorteile der Indizes machen ihre Verwendung zu einem
wichtigen Punkt bei der Datenbankadministration.
Die Verwendung von Indizes bringt aber auch Nachteile mit sich. Der Offensichtlichste dabei ist der zusätzliche Speicherverbrauch eines jeden Index. Dabei gehen
wertvolle Speicherressourcen verloren, ohne das mehr Informationen in der Datenbank
abgelegt sind. Wie viel Speicher genau dabei verloren geht, hängt von der konkreten
Tabelle und des Typs der Spalte ab. Das dies aber ein beträchtlicher Teil sein kann, ist
in Abbildung 1.1 zu sehen. Die dargestellten Beispiele stammen aus der Datenbank
des TPC-H Benchmarks (siehe Kapitel 3.3).
Abbildung 1.1: Beispiele der Größenverhältnisse zwischen Tabelle und Index
2004-09-30/101/IN99/2254
KAPITEL 1. EINLEITUNG
7
Ein weiterer, nicht zu unterschätzender, Nachteil von Indizes ist der zusätzliche
Aufwand für das Datenbanksystem selbst. Dieser Mehraufwand entsteht bei jeder Veränderung an einer Tabelle, welche Indizes enthält. Ursache dafür ist, dass die Modifikationen an der Tabelle ebenfalls in die Indizes übertragen werden müssen. Der dritte
Nachteil betrifft die Ausführungsplanung der Anfragen. Bei dieser Planung müssen
existierende Indizes berücksichtigt werden, nur so können sie der Datenbank einen
Vorteil bringen. Je mehr Indizes angelegt sind, desto mehr mögliche Pläne gibt es, was
sich in längeren Planungszeiten niederschlägt.
Damit die Vorteile der Indizes ihre Nachteile überschatten, sollte das Anlegen von
Indizes sparsam und wohl durchdacht erfolgen. Ein Vorgehen wie das Anlegen eines
Index für jede Spalte in der Datenbank ist somit schon zum Scheitern verurteilt und
würde der Datenbank nicht den gewünschten Geschwindigkeitsgewinn bringen. Indizes dürfen nur auf Spalten angelegt werden, bei denen dies auch angebracht ist. Das
wichtigste Entscheidungskriterium für das Anlegen von Indizes ist die Zugriffshäufigkeit. Mit diesem Wert wird ausgesagt, wie oft eine Spalte innerhalb aller Anfragen
auftaucht und mit einer Bedingung verknüpft ist. Sie ist also „aktiver Bestandteil” einer
Anfrage.
In heutigen Datenbanksystemen kommt die Aufgabe der Indexverwaltung dem Datenbankadministrator zu. Diese nicht automatisierte Indexverwaltung kann als „manuelle Planung” bezeichnet werden kann. Die Verwaltung beinhaltet die Planung, das
Anlegen und die Pflege aller Indizes. Mit einem genauen Wissen über die Struktur
der Datenbank und der Anfragen kann der Administrator geeignete Indizes auswählen und anlegen. Alle angelegten Indizes werden dabei als Index-Set bezeichnet. Dem
Administrator wird es dabei aber nur schwer möglich sein, den zusätzlichen Speicherverbrauch und Mehraufwand der Indizes in seine Planung mit einzubeziehen. Je mehr
Tabellen und Spalten eine Datenbank hat, desto schwieriger wird die Index-Planung
„von Hand”. Ein Beispiel dafür ist die Datenbank der Software „SAP R/3”. Sie umfasst über 13.000 Relationen [17]. Eine solche Menge von Tabellen ist nur schwer zu
überblicken und eine manuelle Index-Optimierung unmöglich.
Kommt zu der steigenden Datenbankkomplexität noch die Unwissenheit über mögliche Anfragen an die Datenbank hinzu, ist eine „manuelle Planung” praktisch nicht
mehr durchführbar. Die Annahme der Unwissenheit über die Anfragen ist dabei nicht
so abwägig, wie es scheint. Sowohl die Datenbank, als auch die Benutzer können sich
im Laufe der Zeit ändern. Daraus resultieren völlig neue Zusammenstellungen von
Anfragen und damit Zugriffshäufigkeiten auf die Spalten der Datenbank. Auch eine
alternierende Datenbanknutzung ist denkbar. So könnte die Nutzung am Tag durch
Auslese-Anfragen geprägt sein und die Nacht durch Update-Anfragen, um die Daten
auf den neuesten Stand zu bringen.
Bei diesem Problem ist der Grund für „anfragengetriebes Index-Tuning” zu finden. Ziel dieses Verfahrens ist die automatische Bestimmung von nützlichen Indizes,
das Anlegen dieser und die Pflege des Index-Set. Das Verfahren arbeitet dabei parallel
zur Datenbank und passt die Indizes kontinuierlich an die Zugriffshäufigkeiten an. Damit kann das anfragengetriebene Index-Tuning als „automatische Planung” der Indizes
2004-09-30/101/IN99/2254
KAPITEL 1. EINLEITUNG
8
verstanden werden. Erreicht werden soll, dass zu jeder Zeit ein optimales Index-Set
vorliegt. Des Weiteren ist es möglich, bei der Auswahl von Indizes Parameter vorzugeben, welche eingehalten werden müssen. So kann der von den Indizes verwendete
Speicher begrenzt werden, als auch das notwendige Verhältnis von Vorteilen zu Nachteilen beschrieben werden. Aus der „automatischen Planung” resultiert eine Entlastung
des Datenbankadministrators und Performancesteigerung des Datenbanksystems.
Dabei ist Index-Tuning ein Teilstück eines Self-Tuning-Datenbanksystems. Dieses
verfügt u.a. über eine automatische Memory-, Disk- und Konfigurations-Optimierung.
So kann sich das Datenbanksystem an die Anforderungen kontinuierlich anpassen.
1.2
Ziel der Diplomarbeit
Ein bestehendes Datenbanksystem soll um einen so genannten „Indexadvisor” erweitert werden. Der „Indexadvisor” (im folgenden auch als „Advisor” bezeichnet) stellt
einen wichtigen Baustein des anfragengetriebenen Index-Tuning dar. Seine Aufgabe
besteht in der Analyse jeder Anfrage, die an die Datenbank gestellt wird. Dabei soll
der „Advisor” nützliche Indizes für die Anfrage ermitteln. Außer der Aussage, welche Indizes nützlich sind, muss der mögliche Performancegewinn abgeschätzt werden.
Diese Informationen können dann als Entscheidungsgrundlage für die Erstellung von
Indizes genutzt werden.
Als Datenbanksystem wurde PostgreSQL gewählt. In dieses wird der „Indexadvisor” implementiert und zwischen dem Rewriter und dem Optimieren eingegliedert.
PostgreSQL ist ein Open-Source-Projekt, d.h. der Quelltext der Datenbank ist frei verfügbar. Dies macht die Einbindung des „Advisors” erst möglich. Zu den weiteren Vorteilen des Datenbanksystems gehört seine weite Verbreitung und damit seine große
Akzeptanz, als auch der sehr gute Optimierer, welcher eine große Bedeutung für den
„Advisor” hat.
1.3
Index-Set Bestimmung
Die Bestimmung eines optimalen Index-Sets für eine Anfrage ist sehr aufwendig. Das
Laufzeitverhalten eines solchen Algorithmus ist exponentiell in Abhängigkeit zu der
Anzahl der Attribute. Dies resultiert aus den stark ansteigenden Kombinationsmöglichkeiten zur Bildung des Index-Sets.
Die Bestimmung eines optimalen Index-Sets ist also ein NP-vollständiges Problem. Zum Beweis kann es leicht in das „Rucksack-Problem” überführt werden. Durch
die schlechte Skalierbarkeit dieser Probleme können nicht beliebig große Datenmengen verarbeitet werden. Für die Bestimmung eines Index-Set, bedeutet dies: Ab einer
bestimmten Anzahl von Attributen ist die Berechnung zu aufwendig. Dies gilt insbesondere, wenn die Berechnung parallel zur Anfrageverarbeitung erfolgt.
Um auch bei aufwändigen Anfragen mit vielen Attributen ein Index-Set bestimmen
2004-09-30/101/IN99/2254
KAPITEL 1. EINLEITUNG
9
zu können, werden unterschiedliche Lösungsansätze benutzt. Diese werden im Kapitel
2 genauer vorgestellt. Allen gemeinsam ist, dass sie nicht alle möglichen Kombinationen berücksichtigen. Damit können sie auch nicht garantieren, dass ein optimales
Index-Set bestimmt wird. Dennoch liefern sie gute Ergebnisse und bieten eine gute
Skalierbarkeit.
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
10
Kapitel 2
Existierende Systeme
2.1
Oracle Virtual Index Wizard
Zur der Datenbank „Oracle” ist eine Sammlung von Zusatzprogrammen verfügbar,
welche zum „Oracle Tuning Pack” zusammengefasst sind. Ein Programm des „Tuning
Packs” ist der „Virtual Index Wizard”. Der Wizard soll bei der Auswahl von nützlichen Indizes für eine Anfrage helfen. Für diese Aufgabe benötigt er lediglich die Anfrage, den Namen der Tabelle und der Spalte, welche auf ihre Eignung für einen Index
überprüft werden soll. Mittels dieser Informationen kann der Wizard seine Aufgabe
durchführen. Er benutzt dabei den Optimierer der Datenbank und einen so genannten
„virtuellen Index”. Dieser Index-Typ simuliert einen „realen Index” und ermöglicht
die Abschätzung des möglichen Nutzen des Index. Das Ergebnis des „Virtual Index
Wizard” ist eine Gegenüberstellung der Ausführung der Anfrage mit und ohne einem
Index, wie in Abbildung 2.1 zu sehen ist. Somit wird es dem Benutzer ermöglicht,
schnell und einfach mögliche Geschwindigkeitsvorteile zu ermitteln und gegebenenfalls Indizes anzulegen [4, 5].
2.2
Microsoft SQL Server Index Selection Tool
Seit der Version 7.0 verfügt der „Microsoft SQL Server” über das „Index Selection
Tool”. Die Aufgabe des Tools ist die Ermittlung von nützlichen Indizes für eine Gruppe von Anfragen, welche auch als „Workload” bezeichnet wird. Das Ergebnis ist ein
Index-Set, welches eine Menge von nützlichen Indizes für den „Workload” darstellt.
Bei der Ermittlung des Index-Set können Grenzen vorgegeben werden, welche vom
„Index Selection Tool” eingehalten werden. Solche Grenzen sind zum Beispiel die
maximale Anzahl der Indizes oder der Speicherverbrauch des Index-Set.
Eine Besonderheit des Tools ist die Berücksichtigung von Multi-Column-Indizes.
Diese Indizes beschränken sich nicht auf eine Spalte, sondern indizieren mehrere
Spalten gleichzeitig. Ihre Ermittlung ist wesentlich rechenintensiver als die Bestimmung von Single-Column-Indizes. Um die benötigte Rechenzeit in Grenzen zu halten,
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
11
Abbildung 2.1: Oracle - „Virtual Index Wizard” ([4])
werden zur Ermittlung der Multi-Column-Indizes nicht alle möglichen Spaltenkombinationen berücksichtigt, sondern Iterationen genutzt. Die erste Iteration beschränkt
sich auf Single-Column-Indizes. Die zweite Iteration dient zur Ermittlung von TwoColumn-Indizes. Diese berücksichtigt aber nur Spalten, welche als Single-ColumnIndizes in Frage kommen. Nach dem selben Muster laufen die weiteren Iterationen ab.
Durch das iterierte Vorgehen wird die Entscheidungskomplexität sehr stark verringert,
und Multi-Column-Indizes können mit vertretbarem Rechenaufwand ermittelt werden.
Die Ermittlung der nützlichen Indizes ist ein NP-vollständiges Problem. Dadurch
steigt der benötigte Rechenaufwand mit der Anzahl der Attribute exponentiell an. Um
das Problem abzuschwächen, werden die bereits beschriebenen Iterationen eingesetzt.
Des Weiteren erfolgt die Ermittlung des Index-Set in zwei Schritten. Als Erstes wird
für jede Anfrage des Workload ein eigenes Index-Set bestimmt. Erst im zweiten Schritt
folgt die Ermittlung des Index-Set für den gesamten Workload. Dabei werden nur die
Indizes berücksichtigt, welche zuvor auch in einem Anfragen-Index-Set enthalten waren.
Für die Bestimmung des Nutzen eines jeden Index wird der Optimierer des „SQL
Server” genutzt. Dazu wurde der Optimierer um What-if-Indizes erweitert. Diese speziellen Indizes existieren nicht physisch, sondern nur virtuell, d.h. sie werden dem Optimierer vorgetäuscht. Die Verwendung des Optimierers zur Entscheidung über den
Nutzen eines Index hat den Vorteil, dass Konsistenz zwischen der Arbeit des „Index Selection Tool” und der normalen Anfrageverarbeitung besteht. In beiden Fällen kommt das gleiche Kostenmodell zum Einsatz. So ist es ausgeschlossen, dass ein
Index, der vom „Index Selection Tool” empfohlen wird, bei der normalen Anfrageverarbeitung nicht zur Anwendung kommt.
Das „Index Selection Tool” besteht aus insgesamt fünf Funktionsblöcken, welche
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
12
Abbildung 2.2: Architektur des „Index Selection Tool” ([3] / S.2)
in der Abbildung 2.2 grau dargestellt sind. Der Input des Tools ist der „Workload”, also eine Zusammenstellung von Anfragen. Der erste Funktionsblock ist mit „Candidate
index selection” bezeichnet. Er reduziert die Anzahl der Spalten, welche für einen Index in Frage kommen. Dies geschieht durch die Entfernung von Spalten, deren Indizes
keinen oder nur geringen Gewinn versprechen. Der zweite Funktionsblock mit dem
Titel „Configuration Enumeration” wählt aus allen möglichen Index-Kombinationen
diejenige aus, welche die niedrigsten Kosten hat. Dabei werden gleichzeitig die vorgegebenen Parameter beachtet. Der mit „Multi-Column Index Generation” bezeichnete Funktionsabschnitt bildet Multi-Column-Indizes, welche beim nächsten Iterationsschritt des „Index Selection Tool” in Betracht gezogen werden sollen. Die zwei verbleibenden Funktionsblöcke benötigen eine Verbindung zum „SQL Server”. Sie führen
die Nutzen- und Kostenermittlung der Indizes durch. Der „What-if Index Creation”Funktionsblock ist für das Erstellen der „What-if-Indizes” verantwortlich. Anhand der
„What-if-Indizes” werden die Kosten eines für jeden Index und damit die Gesamtkosten eines Index-Set bestimmt. Die Tabelle mit dem Kosten wird im „Cost Evaluation”
Funktionsblock verwaltet. Diese Tabelle wird zur Bestimmung des Index-Set mit den
geringsten Kosten benötigt.
Die Iterationen des „Index Selection Tool” sind über eine gepunktete Linie angedeutet. Sind alle Iterationen durchlaufen, gibt das Tool ein Index-Set als Empfehlung
für den Workload aus. Dieses Ergebnis wird mit „Final Indexes” bezeichnet [3].
2.3
IBM DB2 Advisor
In dem Datenbanksystem DB2 von „IBM” wurde in der Version 6.1 der „IBM DB2
Advisor” eingeführt. Dieser ermittelt für einen Workload ein geeignetes Index-Set.
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
13
Auch der „IBM DB2 Advisor” nutzt für seine Berechnungen den Optimierer des Datenbanksystems. Dies ermöglicht den Verzicht auf ein weiteres Kostenmodell im Advisor und wahrt die Konsistenz zwischen der Ermittlung des Index-Set und dessen
Nutzung durch die Datenbank.
Der prinzipielle Aufbau des Advisors ist in Abbildung 2.3 dargestellt. Dabei sind
die grauen Funktionsblöcke die eigentlichen Funktionen des „IBM DB2 Advisor”. Andere Blöcke stellen Funktionen und Einheiten des Datenbanksystems dar und werden
zur Verdeutlichung der Arbeitsweise des Advisors benötigt.
Abbildung 2.3: Architektur des „DB2 Advisors” ([1] / S.2)
Die favorisierte Benutzungsart des Advisors ist über den „Index SmartGuide”. Dieser ist zusammen mit dem Datenbanksystem im System-Speicher zu finden. Der „Index SmartGuide” ist ein grafisches Benutzerinterface und ermöglicht dem Anwender
eine komfortable Nutzung des Advisors. Die zweite mögliche Nutzungssart des Advisors ist über das Kommando-Zeilen-Programm „Db2Advis”. Hinter diesem Programm
verbirgt sich der eigentliche Advisor. Wird der „Index SmartGuide” genutzt, so steuert
dieser für den Anwender den „Db2Advis”.
Als Input benötigt der Advisor ein Set von SQL-Anfragen, also den Workload,
und eine Statistik welche die Häufigkeit der einzelnen Anfragen beschreibt. Bei der
Nutzung des „Index SmartGuide” wird die Statistik automatisch gebildet. Dazu wird
der SQL-Cache des Datenbanksystems nach den Anfragen des Workloads durchsucht
und die ausgelesene Häufigkeit zur Bildung der Statistik genutzt. Im SQL-Cache befinden sich alle Anfragen, die vor kurzer Zeit ausgeführt wurden. Zusätzlich zu dem
Workload und der Statistik können dem Advisor Parameter übergeben werden. Diese
legen zum Beispiel fest, wie lange der Advisor für die Berechnung arbeiten darf oder
welche Größe das Index-Set nicht überschreiten soll.
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
14
Die Arbeit des Advisors besteht darin, für jede Anfrage des Workloads den Optimierer aufzurufen. Dieser ermittelt eine Index-Empfehlung für die Anfrage und legt
das Ergebnis in den „Advise Tables” ab. Von dort werden die Werte vom Advisor
ausgelesen und zur Bildung des Index-Set genutzt. Die „Advise Tables” dienen damit
als Kommunikationsmittel zwischen dem Advisor und dem Optimierer. Das Ergebnis
des „DB2 Advisors” ist ein Index-Set, welches die Empfehlung für den gegebenen
Workload ist.
Auch der „DB2 Advisor” muss mit dem exponentiellen Wachstum des Rechenaufwands zurecht kommen. Dies löst er jedoch anders als das „Index Selection Tool”
(siehe Kapitel 2.2). Der „DB2 Advisor” benutzt einen randomisierten Algorithmus.
Dieser erstellt aus einer Menge von möglicherweise nützlichen Indizes ein zufälliges
Index-Set. Danach ermittelt er den Geschwindigkeitsvorteil des Index-Set und vergleicht diesen mit dem aktuell besten Index-Set. Ist das Index-Set vorteilhafter, so
überschreibt es das aktuell beste, ansonsten wird es verworfen. Dieser Vorgang wiederholt sich solange bis eine vorgegebene Zeit vergangen ist.
Durch den randomisierten Algorithmus ist die benötigte Zeit immer konstant und
unabhängig von dem Umfang der Datenbank und des Workloads [1].
2.4
QUIET
Das QUIET-System ist eine universitäre Entwicklung und ein Ansatz für anfragengetriebenes Index-Tuning. Dabei steht der Name QUIET für „Query-driven Index Tuning”. Das System nutzt den „DB2 Advisor” (siehe Kapitel 2.3), um das Index-Tuning
durchzuführen. Die Architektur des QUIET-Systems ist in Abbildung 2.4 zu sehen.
Um das QUIET-System in die Anfrageverarbeitung zu integrieren, wurde es als
Middleware zwischen dem Datenbank-Frontend und dem DB2-Datenbanksystem umgesetzt. So müssen alle Anfragen an das Datenbanksystem zuerst das QUIET-System
durchlaufen. Die Anfragen werden vom Frontend über das „JDBC Interface” (Java Database Connectivity) an die Datenbank gegeben. Dort wird die Anfrage vom QUIETSystem abgefangen. Nachdem QUIET seine Arbeit beendet hat, gibt es die Anfrage
über das „JDBC Interface” weiter an die Datenbank.
Der erste Teil des QUIET-Systems ist der „Statistic Gatherer”. Er wird bei jeder Anfrage aufgerufen. Seine Aufgabe ist die kontinuierliche Bildung einer IndexStatistik. Diese gibt Auskunft, wie hoch der Profit von möglichen Indizes ist. Für diese
Aufgabe verfügt der „Statistic Gatherer” über ein eigenes Kostenmodell. Dieses berücksichtigt die einmaligen Kosten bei der Erstellung eines Index, die kontinuierlichen
Kosten seiner Pflege und den Gewinn bei jeder Anfrage. Zur Ermittlung von nützlichen
Indizes wird der Optimierer des DB2-Datenbanksystems verwendet, welcher bereits in
Kapitel 2.3 vorgestellt wurde. Die Empfehlungen des Optimierers muss der „Statistic
Gatherer” aus den „Explain Tables” auslesen. Hat der „Statistic Gatherer” seine Arbeit
beendet, legt er die Index-Empfehlungen in der Datenbank „Index Statistics” ab.
Teil zwei des QUIET-Systems ist der „Configuration Manager”. Er verwaltet den
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
15
Abbildung 2.4: Architektur des QUIET-Systems ([2] / S. 3)
„Index-Pool” der Datenbank. Im „Index-Pool” befinden sich alle vom QUIET angelegten Indizes, dabei unterliegt der Pool einer Speichergrenze, welche nicht überschritten
wird. Der „Configuration Manager” arbeitet ausschließlich mit den Ergebnissen des
„Statistic Gatherer”, welches die Einträge in der Datenbank „Index Statistics” sind.
Anhand dieser Einträge erstellt der „Configuration Manager” neue Indizes oder entfernt unnütze aus dem „Index-Pool”. Diese Aufgabe erledigt er mittels festgelegter
Strategien, welche für eine maximale Auslastung des „Index-Pools” und ein optimales
Index-Set sorgen sollen.
Eine Einsicht in die Arbeit des Systems erlaubt der dritte Teil des QUIET-Systems.
Der „Index Configuration Monitor” bietet einen Einblick in die Statistik, zeigt alle
angelegten Indizes im „Index-Pool”, den Profit eines jeden Index und die Auslastung
des „Index-Pool”. In Abbildung 2.5 ist ein Screenshot des „Configuration Monitor”
abgebildet.
Der „Query Generator” gehört nicht im eigentlichen Sinne zum QUIET-System.
Er dient ausschließlich zum Testen. Dazu generiert er Anfragen für das System und
ermöglicht das Testen unter frei wählbaren Bedingungen [2].
Durch die QUIET-Middleware ist aus dem eigentlichen „DB2 Advisor” ein anfragengetriebenes Index-Tuning-System entstanden. Dieses ist in der Lage, sich verändernden Workloads anzupassen um jederzeit eine optimale Leistung der Datenbank zu
garantieren. Jedoch hat das QUIET-System auch Nachteile. Der erste ist die fehlende
Integration in die Datenbank. Die Middleware kann umgangen werden und würde das
System ungewollt nutzlos machen. Hinzu kommen Performance-nachteile durch die
zusätzliche Middleware und der Nutzung des „DB2 Advisors”, welcher für diese Aufgabe nicht entwickelt wurde. Ein weiterer Nachteil ist die Abhängigkeit vom „DB2
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
16
Abbildung 2.5: „Index Configuration Monitor” des QUIET-Systems ([2] / S. 4)
Advisor”. Sollte sich in der Weiterentwicklung der DB2-Datenbank die Ansteuerung
oder der Funktionsumfang des Advisors ändern, so kann dies direkte Auswirkungen
auf das QUIET-System haben. Eine bessere Lösung als die Nutzung einer Middleware
ist die Integration des QUIET in das Datenbanksystem selbst. Für kommerzielle Datenbanken, wie im Fall von DB2, ist der Quellcode aber nicht frei verfügbar. Dadurch
ist eine Integration nicht möglich.
2.5
Zusammenfassung
Der „Oracle - Virtual Index Wizard”, das „Microsoft - Index Selection Tool” und der
„IBM - DB2 Advisor” stellen gute Ansätze für die automatische Bestimmung von
nützlichen Indizes dar. Ihr Funktionsumfang ist für anfragengetriebenes Index Tuning
aber viel zu gering. Im Besonderen fehlt ihnen:
1. Das Anlegen von Indizes
Die vorgestellten Programme erstellen nur Empfehlungen. Das Anlegen der Indizes muss weiterhin manuell vom Datenbankadministrator erfolgen.
2. Das Löschen von Indizes
Wenn sich der Workload ändert, können bereits angelegte Indizes unnütz werden
und verlangsamen die Datenbank. Solche Indizes müssen manuell gefunden und
entfernt werden, da sie von den Tools völlig unberücksichtigt bleiben.
3. Die „online-Arbeit”
Alle Programme werden unabhängig von der Datenbank gestartet, d.h., sie
werden nicht in die laufende Anfrageverarbeitung einbezogen. Dies wird als
„offline-Arbeit” bezeichnet. Zur Realisierung eines anfragengetriebenen Systems ist allerdings die „online-Arbeit” notwendig.
Das QUIET-System löst diese Nachteile, stellt aber trotzdem keine vollwertige Lösung
dar. Die Kritikpunkte an diesem System sind:
2004-09-30/101/IN99/2254
KAPITEL 2. EXISTIERENDE SYSTEME
17
1. Hoher Overhead
Die Realisierung als Middleware ist eine unzureichende Lösung. Größter Nachteil ist die schlechte Performance. So kann der durch das QUIET-System erzeugte Overhead bei bis zu einer Sekunde pro Anfrage liegen
2. Keine Kombinierbarkeit
Nachteil der Nutzung des DB2-Datenbanksystems ist, dass keine Kombination
mit anderen Optimierungen eines Datenbanksystems möglich ist. Solche Optimierungen sind meist für Open-Source-Datenbanken verfügbar und lassen sich
durch den fehlenden Quellcode von DB2 nicht nutzen.
3. Abhängigkeit von „DB2 Advisor”
Wie in Kapitel 2.3 bereits beschrieben ist das QUIET-System direkt vom „DB2
Advisor” abhängig. Eine Änderung seines Interface kann große Auswirkungen
haben.
Diese Probleme des QUIET haben zu dem Ziel geführt, das System auf eine andere
Datenbank zu portieren. Allerdings hat keine verfügbare Datenbank die notwendigen
Voraussetzungen dafür. Diese sind ein Optimierer mit virtuellen Indizes und ein Tool,
welches Index-Empfehlungen ermitteln kann. Deshalb wurde eine Erweiterung der
Datenbank PostgreSQL um die benötigten Funktionen erwogen.
Die Abbildung 2.6 gibt eine Übersicht aller vorgestellten Systeme, mit ihren spezifischen Eigenschaften.
Anlegen von
Indizes
Entfernen
von Indizes
Arbeitsart
Systemintegration
Virtual Index
Wizard
nein
nein
offline
ja
Index Selection
Tool
nein
nein
offline
ja
DB2 Advisor
nein
nein
offline
ja
QUIET
ja
ja
online
nein
Abbildung 2.6: Übersicht der vorgestellten Systeme
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
18
Kapitel 3
PostgreSQL
PostgreSQL ist das ausgewählte Datenbanksystem für die Implementierung des IndexAdvisors. Die Open-Source-Verfügbarkeit und weite Verbreitung von PostgreSQL machen es zur idealen Plattform. Das Datenbanksystem basiert auf einer Entwicklung
von der „University of California”. PostgreSQL ist eine relationale Datenbank mit
objektorientierten Erweiterungen. Sie unterstützt die SQL-Standards „SQL92” und
„SQL99”. Zu ihrer reichhaltigen Ausstattung gehören unter anderem Transaktionen,
Fremdschlüssel und Views. Hinzu kommt die Möglichkeit der Erweiterung der Datenbank durch den Anwender. So können u.a. neue Datentypen, Funktionen oder IndexMethoden hinzugefügt werden [8].
In den folgenden Unterkapiteln werden wichtige Punkte des PostgreSQLDatenbank-Systems vorgestellt. Das Verständnis dieser Punkte ist wichtig für die spätere Erklärung des Aufbaus und der Arbeitsweise des Index-Advisors. Des Weiteren
werden in den entsprechenden Unterkapiteln auch allgemeine Sachverhalte beschrieben, insoweit sie für das Verständnis hilfreich sind.
3.1
3.1.1
Indizes
Indizes allgemein
Die Indizes sind optionale Elemente einer Datenbank, d.h. die Anfrageergebnisse sind
mit und ohne Indizes identisch. Der einzige Unterschied liegt in der Zeit, die vergeht,
bis das Ergebnis einer Anfrage vorliegt. Mit den richtigen Indizes kann die Verarbeitungszeit einer Anfrage nur einen Bruchteil von der Zeit ohne Indizes betragen.
Indizes stellen einen alternativen Zugriffspfad auf die Tupel einer Relation dar. Zu
diesem Zweck enthält der Index die kompletten Werte eines oder mehrerer Attribute
der Relation. Die Attributwerte dienen zur schnellen Beantwortung einer Suche. Aus
diesem Grund werden die Werte in einer dafür geeigneten Speicherstruktur abgelegt.
Dies kann zum Beispiel ein Binärbaum sein.
Zusätzlich zu den Attributwerten verfügt der Index über Verweise zu den Tupeln
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
19
der indizierten Relation. Anhand der Verweise und Attributwerte können schnell die
gesuchten Tupel aus der Relation bestimmt werden. Dabei müssen lediglich die Werte des Index durchsucht werden und nicht die gesamte Relation. Hinzu kommt die
schnellere Suche durch die optimierte Speicherstruktur des Index.
Ein Index kann allerdings nur dann zur Anwendung kommen, wenn mindestens
eines seiner indizierten Attribute in der Anfrage vorkommt und mit einer Bedingung
verknüpft ist. Die Abbildung 3.1 zeigt ein Beispiel einer Index-Nutzung. Die Anfrage
ist links durch die SQL-Worte „SELECT - FROM - WHERE” dargestellt. Zur Beantwortung der Anfrage wird nicht sofort die zugehörige Relation benutzt, welche rechts
dargestellt ist. Ein komplettes Durchsuchen der Relation würde bedeuten, dass die
gesamte Relation von der Festplatte gelesen und jedes Tupel mit den Bedingungen
der Anfrage verglichen werden muss. Dies kann einen sehr hohen Aufwand darstellen und viele Ressourcen verschwenden, besonders wenn die gesuchten Tupel nur ein
kleiner Teil der eigentlichen Relation sind. Aus diesem Grund wird im Beispiel ein
Index genutzt. Er ist in der Mitte der Abbildung dargestellt und als Binär-Baum aufgebaut. Diese Struktur ermöglicht ein schnelles Auffinden der gesuchten Werte. Sind
die gesuchten Attributwerte gefunden, können anhand der Verweise im Index die entsprechenden Tupel der Relation ausgelesen werden.
Abbildung 3.1: Beispiel einer Index-Nutzung
Die Gründe der Performancesteigerung eines Datenbanksystems durch Indizes
sind:
1. Direkter Tupelzugriff
Mittels eines Index können die gesuchten Tupel einer Relation direkt bestimmt
werden. Ein komplettes Durchsuchen der Relation ist nicht notwendig. Dies ist
insbesondere wichtig, da bei den meisten Anfragen nur ein kleiner Teil der Tupel
einer Relation von Interesse ist. Nur selten werden alle Tupel benötigt. Es profitieren nicht nur Select-Anfragen” von Indizes, auch bei Update-, Insert- und
Delete-Anfragen müssen Tupel innerhalb einer Relation gesucht werden.
2. Sortierter Tupelzugriff
Bei vielen Anfragen ist es notwendig, dass die Tupel einer Relation sortiert vor2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
20
liegen. Der Grund dafür kann datenbankintern oder durch die Anfrage vorgegeben sein. Ein datenbankinterner Grund ist z.B. die Art, wie zwei Tabellen verbunden werden sollen. Einige dieser Verbundoperationen, man spricht dabei von
„Joins”, setzen die Sortierung der Relationen voraus. Der Sortierungsgrund von
Seiten der Anfrage ist der Order-by-Teil der Anfrage. Wird eine Sortierung benötigt, so kann anstatt einer sehr aufwendigen Sortierung der gesamten Relation,
der Index genutzt werden. Über ihn wird die Relation direkt sortiert ausgelesen.
3. „Index-Only” Zugriffe
Der Index enthält die kompletten Informationen der indizierten Attribute. Sind
diese Attribute die einzigen, welche in einer Anfrage vorkommen, so muss kein
Tupel der Relation ausgelesen werden. Die Informationen im Index reichen vollkommen, um die Anfrage zu beantworten.
4. Realisierung von Unique-Attributen
Unique-Attribute sind Attribute, bei denen garantiert wird, dass jeder mögliche
Wert nur genau einmal innerhalb der Attribut-Spalte vorkommt. Solche UniqueAttribute werden zum Beispiel zur eindeutigen Identifikation von Tupel genutzt.
Beim Einfügen eines neuen oder der Modifikation eines Tupels, muss bei einem Unique-Attribut geprüft werden, ob sein Wert nicht schon existiert. Dies
entspricht einem Durchsuchen der gesamten Relation, was sehr lange dauern
kann und Ressourcen verschwendet. Ein effizienterer Weg ist die Indizierung
des entsprechenden Attributs. Dabei ist der Index für die Einhaltung der UniqueBedingung verantwortlich. Dem Index ist es viel schneller möglich zu entscheiden, ob es den Wert innerhalb der Attribut-Spalte bereits gibt.
Neben diesen Vorteilen gibt es auch Nachteile von Indizes. Diese können die gewonnene Performance des Datenbanksystems wieder verringern oder sogar negieren. Die
Nachteile sind im einzelnen:
1. Pflege der Indizes
Indizes enthalten teilweise die kompletten Werte der indizierten Relation. Aus
diesem Grund müssen die Indizes aktualisiert werden, sobald sich der Inhalt der
Relation ändert. Solch eine Aktualisierung ist bei jedem Update, Delete oder
Insert notwendig. Innerhalb des Index kann dies aber mehr als nur die simple
Änderung eines Attributwertes bedeuten. Bei einem solchen Index-Update kann
es möglich sein, dass die Speicherstruktur des Index umgeordnet werden muss.
Nur so kann immer ein effizientes Suchen innerhalb des Index gewährleistet werden. Je mehr Indizes in einer Datenbank sind, desto höher kann die zusätzliche
Arbeit bei der Änderung einer Relation werden.
2. Größe der Datenbank
Die Attributwerte im Index sind redundant, d.h., sie vergrößern die Datenbank
ohne einen Mehrgewinn an Informationen für den Benutzer. Damit erhöht die Indizierung eines Attributs den Speicherverbrauch des Attributs um mehr als das
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
21
Doppelte. So kann ein sehr verschwenderischer Umgang mit Indizes die Datenbankgröße stark ansteigen lassen, was unter der Voraussetzung von begrenzten
Speicherressourcen nicht unbedenklich ist.
3.1.2
Index-Scan
Das Durchsuchen eines Elementes der Datenbank wird als „Scan” bezeichnet. Wird
ein Index durchsucht, so ist dies ein Index-Scan. Dabei müssen nicht alle Werte des
Index gelesen werden. Durch die spezielle Speicherstruktur des Index, können die gewünschten Informationen gezielt ausgelesen werden. Im Gegensatz dazu liegen die
Tupel in einer Relation unstrukturiert vor. Das Suchen in einer Relation geht nur durch
das Einlesen der Relation vom Anfang bis zum Ende. Aus diesem Grund spricht man
bei einem Durchsuchen einer Relation von einem Sequentiellen-Scan.
3.1.3
Index-Typen in PostgreSQL
In PostgreSQL sind mehrere Typen von Indizes verfügbar. Der gewünschte Typ kann
beim Anlegen eines Index spezifiziert werden. Der Unterschied zwischen den einzelnen Typen liegt in der verwendeten Speicherstruktur. Daraus ergeben sich unterschiedliche Eigenschaften, Fähigkeiten und Anwendungsgebiete. Folgende drei Index-Typen
gibt es:
1. „B-Tree” Indizes
Dieser Index-Typ ist der Default-Index-Typ, d.h., wenn beim Erstellen eines Index nicht explizit ein Typ angegeben wird, so wird ein B-Tree-Index angelegt.
Als Speicherstruktur verwendet dieser Typ einen Binär-Baum. Dieser Baum ermöglicht es, die häufigsten Anfragebedingungen effizient zu beantworten. Diese sind Bereichs- und Vergleichsbedingungen. Bereichsbedingungen werden in
SQL mittels des „BETWEEN” Schlüsselwortes formuliert. Ein Beispiel ist „price BETWEEN 5 AND 50”. Zu den Vergleichsbedingungen gehören u.a. die
Operatoren „kleiner als” oder „größer als”. Alle unterstützten Operatoren sind
„<”, „<=”, „=” , „>=” und „>”. Außer den Bereichs- und Vergleichsbedingungen
unterstützt der B-Tree-Index „LIKE-”, „ILIKE”, „~” und „~*”-Bedingungen. So
kann er zum Beispiel genutzt werden, um nach dem Anfang eines Wortes zu suchen, welches mit dem „LIKE” Schlüsselwort möglich ist. Der B-Tree-Index
ermöglicht auch das sortierte Auslesen von Tupel. Dabei werden mehrere Sortierungsarten unterstützt.
2. „R-Tree” Indizes
Der R-Tree-Index-Typ hat nur ein sehr spezielles Anwendungsgebiet. Er wird
für Anfragen mit räumlichen Koordinaten genutzt. Diese sind zum Beispiel
„Box-Koordinaten”. Dabei kann ermittelt werden, ob sich die Koordinaten überlappen, gegenseitig enthalten oder identisch sind.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
22
3. „Hash” Indizes
Ein Hash-Index kann nur zur Beantwortung der Frage nach Gleichheit („=”) benutzt werden. Dies schränkt sein Anwendungsgebiet zwar stark ein, aber besonders für den Verbund von Relationen wird nur diese Bedingung benötigt. Hinzu
kommt die hohe Effizienz des Hash-Index, da die Frage nach Gleichheit meist
direkt beantwortet werden kann. Trotzdem wird der Hash-Index-Typ in PostgreSQL nur selten verwendet, denn laut der PostgreSQL-Dokumentation hat der
Hash-Index, gegenüber dem B-Tree-Index, weder in der Geschwindigkeit, noch
in der Größe des Index einen Vorteil [9].
Der Einsatz der unterschiedlichen Index-Typen unterliegt dem Benutzer. Er kann für
den jeweiligen Einsatzzweck den richtigen Typ wählen. Dies dürfte in den meisten
Fällen der B-Tree-Index sein, was seine Stellung als Default-Index-Typ rechtfertigt.
Auch während der Verarbeitung einer Anfrage werden Indizes erstellt. Dies geschieht
durch das Datenbanksystem und hat den Zweck, die Abarbeitung effizienter zu gestalten. Diese Vorgänge sind für den Benutzer transparent und geschehen nur temporär im
Speicher der Datenbank. Die dabei verwendeten Index-Typen wählt das Datenbanksystem selbst aus.
3.2
Anfrageplanung
Der Umfang einer Anfrage kann sehr einfach sein und nur eine Tabelle mit wenigen Spalten betreffen. Es sind aber auch hochkomplexe Anfragen mit vielen Tabellen,
Spalten und Unteranfragen möglich. Bei jeder Anfrage muss das Datenbanksystem zuerst entscheiden, in welcher Reihenfolge die Anfrage verarbeitet wird. Dieser Vorgang
wird als „Anfrageplanung” bezeichnet. Grund für diese Planung ist, dass nicht alle
Teile der Anfrage gleichzeitig ermittelt werden können. Hängt z.B. eine Bedingung
von dem Ergebnis einer Unteranfrage ab, so muss zuerst die Unteranfrage ausgeführt
werden. Neben solchen Abhängigkeiten gibt es auch die Möglichkeit, dass die Reihenfolge von Operationen vertauscht werden kann, ohne das sich das Ergebnis verändert.
Des Weiteren ist es möglich, dass mehrere Wege existieren, um die benötigten Tupel
zu ermitteln. Dies kann z.B. ein Sequentieller-Scan und ein Index-Scan der Relation
sein.
Die Möglichkeiten, dasselbe Ergebnis zu erziehlen, aber mit unterschiedlichen
Mitteln und Schritten, erhöhen sich mit der Komplexität der Anfrage. Eine konkrete Verarbeitungsreihenfolge aus allen möglichen wird als „Plan” bezeichnet. Ziel der
Anfrageplanung ist es, den „Plan” zu bestimmen, der die niedrigsten Gesamtkosten
aufweist. Die Ermittlung dieses „Plans” ist umso aufwendiger, je komplexer die Anfrage ist.
Zur Reduzierung der Gesamtkosten werden zum Beispiel Operationen, die die
Menge der Tupel stark einschränken, zuerst ausgeführt. Solche Operationen können
Vergleichsbedingungen wie „kleiner als” sein. Erfüllen bei dieser Operation viele
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
23
Tupel die Bedingung nicht, müssen diese in den weiteren Schritten nicht sortiert oder
mit anderen Tupeln verbunden werden. Das Ziel, den günstigsten „Plan” zufinden, ist
dabei sehr aufwendig, denn der Zeitbedarf der Ermittlung steigt exponentiell mit der
Anfragengröße an.
In PostgreSQL wird die Anfrageplanung vom „Optimierer/Planner” durchgeführt.
Dieser erstellt für jede Anfrage einen „Plan”. Der generierte „Plan” ist ein Baum, welcher vom „Executor” abgearbeitet wird. Dabei beginnt dieser bei den Blättern. An
jedem Knoten müssen die Tupel der Zweige verbunden werden. Auf diese Weise entsteht die Ergebnismenge bis zur Wurzel.
Soweit es dem „Optimierer/Planner” möglich ist, erstellt er jeden möglichen „Plan”
und wählt den günstigsten aus. Durch das exponentielle Wachstum der Zeit für die
Anfrageplanung ist dies nicht bei allen Anfragen möglich. Für das Wachstum ist insbesondere die Anzahl der „Joins” verantwortlich. Ist die benötigte Zeit für die Anfrageplaung zu hoch, wird der „Genetic Query Optimizer” eingesetzt. Sein Zeitbedarf
wächst nicht exponentiell, aber der von ihm ermittelte „Plan” muss nicht der „Plan”
mit den geringsten Gesamtkosten sein [10].
3.3
TPC-H Datenbank
In dem Kapitel 3 werden interne Abläufe und Datenstrukturen von PostgreSQL vorgestellt. Diese sind anhand eines Beispiels besser verständlich, als mit aufwendigen
Umschreibungen. Für die Beispiele wird ein Teil der Datenbank des „TPC-H Benchmarks” verwendet.
Der eigentliche Einsatzzweck dieser Datenbank ist die Funktion als Benchmark. Dabei soll der „TCP-H Benchmark” als Entscheidungshilfe für BusinessDatenbanksysteme helfen. Die Datenbank verfügt über eine große Anzahl an Daten,
üblicherweise 1GB, und stellt mit den hoch komplexen Anfragen hohe Anforderungen
an das Datenbanksystem. Die Resultate des Benchmarks sollen als Vergleichsmittel
zwischen verschiedenen Datenbanksystemen dienen. Dabei sollen die ausgewählten
Anfragen die Relevanz des Ergebnisses für den Einsatz in einem Unternehmen sicherstellen [16].
Das vollständige Schema der „TPC-H Datenbank” ist in Abbildung 3.2 dargestellt.
Die Anzahl der Relationen und Attribute ist allerdings viel zu groß, um sie alle in den
Beispielen zu nutzen. Deswegen beschränken sich die Beispiele auf zwei Relationen
und insgesamt sechs Attribute, welche in der Abbildung 3.2 grau dargestellt sind. Die
verwendeten Relationen sind:
• Relation „orders”
Diese Relation enthält, wie aus dem Namen zu schließen ist, Bestellungen. Dabei sind die Attribute „orderkey”, „orderdate” und „comment” für die Beispiele interessant. Das Attribut „orderkey” enthält den Identifikator der Bestellung.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
24
Abbildung 3.2: Schema der TPC-H Datenbank ([16] / S.12)
Des Weiteren dient dieser auch als Fremdschlüssel in anderen Relationen. Das
Datum der Bestellung wird in „orderdate” gespeichert. Ein Kommentar zu der
Bestellung kann in dem Attribut „comment” abgelegt werden. In der gesamten „TPC-H Datenbank” erhalten alle Attribute vor ihren Namen den Anfangsbuchstaben der Relation. Dies macht die Attributnamen eindeutig in der gesamten Datenbank. Demzufolge lauten die korrekten Attributnamen „o_orderkey”,
„o_orderdate” und „o_comment”.
• Relation „lineitem”
Die Zuordnung von Waren zu einer Bestellung geschieht über diese Relation.
Dabei dient das Attribut „orderkey” als Bezug zur Relation „orders”, also zu der
Bestellung selbst. Das Attribut „partkey” ist ein Fremdschlüssel und steht für
die Ware. Die Menge der bestellten Ware wird in „quantity” gespeichert. Auch
vor diese Attribute muss der Anfangsbuchstabe der Relation gesetzt werden. Die
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
25
Namen lauten also „l_orderkey”, „l_partkey” und „l_quantity”.
Der gewählte Datenbankausschnitt ist im Vergleich zur gesamten Datenbank recht
klein. Die ausgewählten Relationen und Attribute reichen aber völlig aus und machen
die Beispiele überschaubarer. Die Nutzung von allen Attributen der Relation „lineitem” (16 Stück) und der Relation „orders” (9 Stück) würde weder zum Verständnis,
noch zur Übersichtlichkeit beitragen.
3.4
Der Weg einer Anfrage
Innerhalb des PostgreSQL-Datenbanksystems durchläuft eine Anfrage mehrere Verarbeitungsstufen bis das Ergebnis vorliegt. Man spricht dabei auch von einer
Verarbeitungs-Pipeline. Dabei hat jede der Verarbeitungsabschnitte eine festgelegte
Aufgabe. Einen Gesamtüberblick über die Verarbeitungsstufen und ihre Reihenfolge
gibt die Abbildung 3.3. Die wichtigsten Verarbeitungsschritte werden im Folgenden
beschrieben. Sie sind in der Abbildung innerhalb des größten grauen Rechteckes zu
finden.
• „Parser”
Jede Anfrage erreicht das PostgreSQL-Datenbanksystem als String. Diesen
String überprüft der Parser auf seine syntaktische Korrektheit. Findet er keine
Fehler, erzeugt er anhand des Strings einen Baum, den so genannten QueryTree. Dieser repräsentiert die textuelle Anfrage, hat aber selbst eine baumartige
Speicherstruktur. Die Wurzel des Baumes gibt darüber Auskunft, um was für eine Anfrage es sich handelt. Ein Beispiel dafür ist ein Select-Statement, also eine
Anfrage, die mit dem „SELECT” Schlüsselwort beginnt und aus der Datenbank
nur Daten ausliest. Von der Wurzel aus verzweigt sich der Baum. Die einzelnen
Zweige enthalten weitere Informationen zu der Anfrage. Zum Beispiel existiert
je ein Zweig, um die Informationen der „Where”- und „Group by”-Klausel zu
speichern. Der entstandene Query-Tree ist die Grundlage für alle weiteren Verarbeitungsschritte.
• „Transformation”
Dieser Abschnitt wird in der Abbildung 3.3 als „Traffic Cop” bezeichnet. In dieser Verarbeitungsstufe werden die Namen der Relationen und Attribute in ihre
Identifikatoren umgewandelt. Nur mit diesen kann das Datenbanksystem umgehen.
Des Weiteren findet eine Trennung der Anfragen statt. Bei der Trennung werden komplexe Anfragen, wie z.B. Select-, Update- oder Delete-Anfragen an die
„normale” Verarbeitungspipeline weitergegeben. Einfache Anfragen, wie z.B.
Create-, Analyze- oder Drop-Anfragen hingegen werden von den sogenannten
„Utility Commands” ausgeführt. Der Grund der Trennung liegt in dem notwendigen Aufwand für die Anfragen. Für einfache Anfragen ist es nicht notwendig,
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
26
Abbildung 3.3: Verlauf der Anfrageverarbeitung [7]
eine Optimierung durchzuführen, auch unterliegen sie nicht „Views”. Diese Aufgaben werden im Weiteren noch genauer beschrieben.
• „Rewrite”
Das PostgreSQL-Datenbanksystem ermöglicht die Verwendung von so genannten „Views”. Mittels dieser können Relationen und ihre Attribute zu „neuen
Relationen” zusammengestellt werden, man spricht dabei von „Views”. Die
„Views” existieren nicht in der Datenbank, sondern werden für den Benutzer
nur simuliert. Aktionen an diesen Relationen müssen auf die „physischen Relationen” umgelenkt werden. Diese Aufgabe unterliegt dem „Rewrite System”.
Falls die Anfrage auf einem „View” basiert, schreibt das „Rewrite System” die
Anfrage um. Im Ergebnis des Verarbeitungsschrittes existieren nur noch „physi-
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
27
sche Relationen” im Query-Tree.
• „Optimizer”
Die Aufgabe des Optimierers wurde bereits ausführlich im Kapitel 3.2 beschrieben. Die Arbeit dieses Abschnittes der Pipeline besteht im Wesentlichen in der
Erstellung von Ausführungsplänen und der Auswahl des Planes mit den geringsten Gesamtkosten. Aus diesem Grund ist der Verarbeitungsschritt in der Abbildung 3.3 mittels der zwei Blöcke „Generate Paths” und „Choose Path & Generate Plan” dargestellt. Das Ergebnis ist ein neuer Baum, der Plan-Tree. Dieser
ersetzt den Query-Tree und repräsentiert nicht mehr die Anfrage. Die Struktur
des Plan-Tree stellt die Reihenfolge dar, in welcher der „Executor” die Anfrage
abarbeiten soll.
• „Executor”
Dieser Verarbeitungsschritt ist der letzte der Pipeline. Anhand des Plan-Trees
führt er die Verarbeitung der Anfrage durch und bildet die Ergebnismenge. Dazu durchläuft er den Plan-Tree rekursiv, beginnend von der Wurzel. Mit dem
Auslesen von Tupel beginnt der „Executor” aber erst, wenn er bei einem Blatt
angelangt ist. Zum Schluss wird die Ergebnismenge vom Executor an den Aufrufenden zurückgegeben [6, 7].
3.5
List-Package
Listen sind unabdingbare Datenstrukturen in fast jedem Programm, so auch im
PostgreSQL-Datenbanksystem. Dort werden sie u.a. zur Speicherung von Parametern,
einer Menge von Tupeln oder als Teil einer Baumstruktur eingesetzt.
Zur Implementierung von PostgreSQL wird die Hochsprache „C” genutzt. Sie stellt
keine vollwertigen Listen zur Verfügung. In „C” sind nur Arrays mit fester Länge und
einheitlichem Typ möglich. Aus diesem Grund existiert eine List-Package in PostgreSQL. Diese „Package” ermöglicht das Erstellen und die Verwendung von einfachverketteten Listen. Dafür stellt sie vielfältige Funktionen bereit. Diese ermöglichen
z.B. das Suchen innerhalb einer Liste, das Verbinden und Vergleichen von Listen und
das Entfernen von Elementen aus einer Liste.
Die Listen werden mittels Zeiger zwischen ihren Elementen aufgebaut. Dadurch ist
die Länge einer Liste nur durch den verfügbaren Speicher begrenzt. Die List-Package
stellt zwei Listentypen bereit. Der erste Typ verfügt nur über einen Zeiger auf den
Listenanfang, den so genannten „Head”. Eine solche Liste ist in der Abbildung 3.4
dargestellt. Operationen am Ende der Liste können nicht direkt ausgeführt werden.
Dazu muss zuerst die komplette Liste durchlaufen werden. Aus diesem Grund gibt es
den zweiten Listentyp, der in PostgreSQL auch als „FastList” bezeichnet wird. Dieser Typ hat zwei Zeiger auf die Liste, einen auf den „Head” und einen auf das Ende
der Liste, dem so genannten „Tail”. Der Zeiger auf den „Tail” macht besonders bei
der Arbeit mit sehr langen Listen Sinn. Bei solchen Listen kann das Durchlaufen der
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
28
kompletten Liste viel Zeit in Anspruch nehmen, was mit dem Tail-Zeiger vermieden
wird.
Abbildung 3.4: List-Package Beispiel
Wie bereits kurz erwähnt, ist in der Abbildung 3.4 eine Beispiel-Liste vom ersten
Typ, also eine „normale” Liste, dargestellt. Die Variable „dataList” speichert den Zeiger auf den Listenanfang. Dieser Zeiger verweist auf das erste Element der Liste, dabei
sind die Listenelemente immer Listen-Nodes. Diese Nodes erlauben die Speicherung
eines Zeiger-, Integer- und Oid-Wertes. Die Nutzung der drei Werte ist beliebig, es
können auch alle drei Felder gleichzeitig benutzt werden. In dem Integer- und OidFeld können Zahlenwerte abgelegt werden. Dabei ist Integer selbsterklärend und Oid
steht für Object-id, was ein in PostgreSQL häufig verwendeter Typ ist. Er wird zur
Identifikation von Relationen, Tupeln oder Operationen vergeben. In dem Zeiger-Feld
kann ein beliebiger Zeiger abgelegt werden. Diese Möglichkeit dient dazu, in der Liste
beliebige Informationen abzulegen. Dies wurde auch in dem Beispiel in Abbildung 3.4
genutzt. Die mit „Data1”, „Data2” und „Data3” bezeichneten Blöcke stellen die in der
Liste abgelegten Informationen dar.
Jeder der List-Nodes verfügt über ein Feld mit dem Namen „next”. Dieses Feld
enthält einen Zeiger auf das nächste Element der Liste. Ist das Listenende erreicht,
wird dies mit einem Null-Zeiger symbolisiert. Dieser ist in der Abbildung 3.4 mit
einem „x” verdeutlicht.
Die Deklaration des List-Node-Typs ist in Abbildung 3.5 zu sehen. Sie besteht aus
dem „type-”, „union-” und „next-Feld”. Der „type” dient zur Identifikation des ListNodes und weist bei allen List-Nodes den selben Wert auf, dieser ist „T_LIST”. Das
„union-Feld” stellt die möglichen „Speicherplätze” bereit, welche bereits beschrieben
wurden. Der Verweis auf das nächste Element der Liste ist im „next-Feld” enthalten.
Die List-Package ermöglicht das einfache Anlegen und Arbeiten mit Listen. Durch
die einheitliche Struktur können die Listen auch als Parameter von Funktionen genutzt
werden. Bei der Nutzung des Integer- oder Oid-Feldes stehen des Weiteren eine Fülle von unterstützenden Funktionen bereit. Mit diesen können u.a. Werte in der Liste
gesucht, ausgelesen und entfernt werden.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
29
typedef struct List
{
NodeTag type;
union
{
void *ptr_value;
int int_value;
Oid oid_value;
} elem;
struct List *next;
} List;
Abbildung 3.5: Definition des List-Nodes
3.6
Trees
Bei der Verarbeitung einer Anfrage durch das PostgreSQL-Datenbanksystem (siehe
Kapitel 3.4) werden mehrere Bäume erzeugt. Die Bäume, auch als Trees bezeichnet,
repräsentieren die Anfrage und dienen zur Speicherung von Verarbeitungsergebnissen. Sie sind die zentralen Speicherstrukturen bei der Verarbeitung. Auch der IndexAdvisor nutzt die Trees. Aus diesem Grund ist eine genaue Kenntnis der Bäume wichtig für das Verständnis der Arbeitsweise des Index-Advisors. In den folgenden Unterkapiteln werden die in PostgreSQL vorkommenden Bäume vorgestellt. Dabei werden
zuerst die Nodes beschrieben, aus denen sich jeder Baum zusammensetzt.
3.6.1
Nodes
Die Nodes (zu deutsch „Knoten”) sind die eigentlichen Informationsträger eines jeden
Baumes in PostgreSQL. Der Aufbau eines Nodes ist in der Abbildung 3.9 beispielhaft
dargestellt. Ein Node besteht im Allgemeinen nur aus einer Sammlung von Variablen.
Je nach der Aufgabe des Nodes schwankt die Anzahl und der Typ der Variablen. Im
Verlauf der Anfrageverarbeitung werden die Werte der Variablen gesetzt, ausgelesen
und modifiziert. Dies geschieht u.a. vom „Parser”, „Optimizer” und „Executor”, welche bereits im Kapitel 3.4 vorgestellt wurden. Die Modifikation der Variablen dient der
Speicherung und Weitergabe von Verarbeitungsergebnissen.
Eine besondere Bedeutung kommt den Variablen vom Typ „NodeTag” und „Pointer” zu. Im „NodeTag”, welcher nur einmal pro Node vorkommen darf, wird der Typ
des Nodes festgelegt. Vor jeder Arbeit mit einem Node, muss der „NodeTag” ausgelesen werden, denn dieser gibt Auskunft über die Struktur des Nodes. Das bedeutet anhand des „NodeTag” kann der Typ eines jeden Nodes ermittelt werden. Variablen vom
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
30
Typ „Pointer” dienen u.a. zur Bildung einer Baumstruktur. Dazu können die „Pointer”
eines Nodes auf andere Nodes zeigen. Dies ermöglicht den Aufbau beliebig großer
und komplexer Baumstrukturen.
Aufgrund der vielfältigen Einsatzmöglichkeiten der Nodes gibt es viele verschiedene Typen. Diese sind in PostgreSQL in Kategorien eingeteilt. Dabei sind die Kategorien nach dem Einsatzzweck des Nodetyps benannt. Diese sind:
• „Primenodes”
Diese Nodes können in jedem Baum vorkommen. Damit bilden die Primenodes
eine Art Basis. Auch werden viele Nodes von den Primenodes abgeleitet. Eine
Ableitung ist dabei mit einer Vererbung in der objektorientierten Programmierung vergleichbar. Bei einer Ableitung erhält der neue Node alle Variablen eines
Primenodes und zusätzlich können weitere hinzugefügt werden.
Ein Beispiel für einen Primenode ist der Executable-Expression-Node oder kurz
Expr-Node. Seine Definition ist in Abbildung 3.6 dargestellt. Viele Nodes werden vom Expr-Node abgeleitet. Die Definition des Expr-Nodes besteht nur aus
dem „NodeTag”. Dieser hat keine Bedeutung zur Speicherung von Verarbeitungsinformationen. Erst die abgeleiteten Nodes von diesem Typ können für die
Anfrageverarbeitung genutzt werden. Trotzdem hat der Expr-Node seine Berechtigung. Durch ihn ist es jederzeit möglich, alle abgeleiteten Nodes mit zusätzlichen Variablen zu versehen.
typedef struct Expr
{
NodeTag
type;
} Expr;
Abbildung 3.6: Definition des Expr-Nodes
• „Parsenodes”
Nodes dieser Kategorie werden vom Parser zum Aufbau des Query-Tree benötigt. Der Parser bildet aus der Anfrage, welche in Textform vorliegt, den QueryTree. Für diesen Zweck werden Nodes benötigt, welche die Abschnitte, Schlüsselwörter und Funktionen des SQL-Standards repräsentieren. So gibt es z.B.
einen Node für eine Insert-Anfrage, den InsertStmt-Node und einen Node für
die Modifizerung einer Tabelle, den AlterTableStmt-Node.
• „Plannodes”
Der Optimierer erstellt anhand des gerade beschriebenen Query-Trees, den PlanTree. In dem Plan-Tree kommen die Plannodes zur Anwendung. Diese Kategorie von Nodes ist notwendig, da die Struktur und der Inhalt des Plan-Tree völlig
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
31
verschieden zum Query-Tree ist. Jeder der Plannodes ist vom Node „Plan” abgeleitet.
Dessen Definition ist in Abbildung 3.7 dargestellt. Dabei sind nur die wichtigsten Variablen aufgeführt. Diese sind der bereits erklärte „NodeTag”, die Kosten „startup_cost” und „total_cost” sowie die Größe der Ergebnismenge, mit
„plan_rows” und „plan_width” bezeichnet.
Die zwei Kostenvariablen sind eine Schätzung des Optimierers und dienen zur
Auswahl des günstigsten „Plans”. Die Variable „startup_cost” bezeichnet dabei
die Kosten, die minimal benötigt werden, um die Anfrage zu beantworten. In Abhängigkeit von der Menge der Tupel, welche für die Anfrage ausgelesen werden
müssen, können die Kosten bis zu dem Wert der Variablen „total_cost” steigen.
Eine Schätzung über die Tupelanzahl ist die Variable „plan_rows”. Dabei gibt
der Wert „plan_width” die ungefähre Anzahl an Bytes an, die jedes ErgebnisTupel haben wird.
Beispiele für Plannodes sind der IndexScan-Node und der MergeJoin-Node. Der
IndexScan-Node liefert alle Angaben zur Benutzung eines Index, um Tupel aus
einer Relation auszulesen. Der MergeJoin-Node beschreibt, wie Tupel aus verschiedenen Relationen miteinander verbunden werden sollen. Die Informationen
dieser Nodes werden von „Executor” ausgewertet und entsprechend genutzt.
typedef struct Plan
{
NodeTag
type;
Cost
startup_cost;
Cost
total_cost;
double
plan_rows;
int
plan_width;
...
} Plan;
Abbildung 3.7: Definition des Plan-Nodes
• „Execnodes”
Wie sich anhand des Namen vermuten lässt, werden die Execnodes vom „Executor” eingesetzt. Dieser benötigt die Nodes zur Speicherung von Statusinformationen. Der „Executor” arbeitet den Plan-Tree der Reihe nach ab. Dabei entstehen Daten, für deren Aufnahme die Nodes im Plan-Tree nicht vorgesehen sind.
Um die Informationen trotzdem ablegen zu können, baut der „Executor” einen
weiteren Tree auf, dies ist der State-Tree.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
32
Der State-Tree besteht aus den Execnodes. Dabei existiert für viele Node-Typen
der Plannodes ein korrespondierender Node-Typ in den Execnodes. Beim Aufbau des State-Trees legt der Executor Verweise zwischen den meisten Nodes des
Plan-Tree und den zugehörigen Nodes des State-Tree an. Informationen die während der Verarbeitung eines Nodes des Plan-Tree anfallen, werden vom Executor
in den korrespondierenden Node im State-Tree abgelegt. Dieser Zusammenhang
ist in Abbildung 3.8 dargestellt. Dabei symbolisieren die gepunkteten Pfeile die
Verweise zwischen den zwei Trees.
Beispiele für Execnodes sind der SubPlanState-Node und der LimitState-Node.
Der SubPlanState-Node korrespondiert mit dem SubqueryScan-Node und enthält die Informationen zu einer Unteranfrage. Der zum LimitState-Node gehörige Plannode ist der Limit-Node. Dieser ist für die Begrenzung der Ergebnismenge verantwortlich, was dem SQL-Schlüsselwort „LIMIT” entspricht.
Abbildung 3.8: Zusammenhang zwischen State-Tree und Plan-Tree
An einem Beispiel soll veranschaulicht werden, wie ein Node aufgebaut ist. Dazu wird
der SortBy-Node genauer betrachtet. Seine Definition ist in Abbildung 3.9 zu sehen.
Der Node ist ein Parsenode und wird im Query-Tree verwendet. Der „Parser” nutzt
diesen Node-Typ bei der Umsetzung der „Order by-Klausel” einer Anfrage.
Die Variablen des SortBy-Nodes bedeuten im einzelnen:
• „type”
Dies ist der „NodeTag”. Er dient zur Identifikation des SortBy-Nodes und muss
immer den Wert „T_SortBy” haben.
• „sortby_kind”
Der Wert dieser Variable legt die Sortierungsart fest. Die dabei möglichen Werte
werden über Konstanten festgelegt. Es gibt je eine Konstante für aufsteigende,
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
33
typedef struct SortBy
{
NodeTag type;
int
sortby_kind;
List
*useOp;
Node
*node;
} SortBy
Abbildung 3.9: Definition des SortBy-Nodes
absteigende und spezielle Sortierung. Dabei ist mit der „speziellen Sortierung”
eine vom Anwender angelegte Sortierart gemeint.
• „useOp”
Dieser Zeiger verweist auf den Operator, der zur Sortierung genutzt werden soll.
Ein Operator dient bei der Sortierung zur Ermittlung der Beziehung zweier Werte. Das Ergebnis des Operators ist die Aussage, welcher der zwei Werte größer
ist. Eine ausführlichere Beschreibung der Operatoren ist in Kapitel 3.9.2 zu finden.
• „node”
Die Variable „node” ist ebenfalls ein Zeiger. Sie verweist auf die Informationen
zum Attribut, nach welchem die Sortierung erfolgen soll.
Die Menge der verschiedenen Node-Typen ist sehr groß. Dies zeigt, wie komplex die
Verarbeitungspipeline des PostgreSQL-Datenbanksystems ist. Durch die Einteilung
der Nodes in die Kategorien, bleibt jede Kategorie für sich dennnoch überschaubar.
Der eigentliche Verwendungszweck der Nodes wird in den folgenden Unterkapiteln
noch näher beschrieben.
3.6.2
Query-Tree
Jede Anfrage an das PostgreSQL-Datenbanksystem wird als Text gestellt. Diese Form
der Anfrage ist ungeeignet für die weitere Verarbeitung. Aus diesem Grund wird der
Text vom „Parser” in den Query-Tree umgesetzt. Dieser repräsentiert die Anfrage vollständig und wird bei den weiteren Verarbeitungsschritten genutzt.
Der Tree besteht selbst aus Nodes, welche bereits im Kapitel 3.6.1 vorgestellt wurden. Die Nodes verfügen über Variablen, die vom Typ „Pointer” sein können. Anhand
dieser Zeiger lassen sich beliebige Strukturen mit den Nodes aufbauen, u.a. auch Bäume.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
34
SELECT l_orderkey,count(l_partkey)
FROM orders,lineitem
WHERE o_orderkey = l_orderkey
AND o_orderdate < ’1998-06-20’
GROUP BY l_orderkey
HAVING count(l_partkey) > 5;
Abbildung 3.10: Beispiel-Anfrage
Das beste Verständnis der Query-Trees ist anhand eines Beispiels möglich. Im Abbildung 3.10 ist der SQL-Text einer Anfrage dargestellt, welche als Beispiel genutzt
werden soll. Die von der Anfage verwendete Datenbank wurde im Kapitel 3.3 näher
beschrieben.
Das Ergebnis der Anfrage ist die Bestellnummer und Anzahl der Warentypen einer
Bestellung. Dabei sind nicht alle Bestellungen der Datenbank interessant. Es werden
nur diejenigen ausgelesen, bei denen mehr als fünf Warentypen bestellt wurden und
die vor dem 20. Juni 1998 eingegangen sind. Des Weiteren wird nach dem Attribut
„l_orderkey” gruppiert, um die Aggregations-Funktion „count” anwenden zu können.
Ein Teil dieser Ergebnismenge ist in Abbildung 3.11 dargestellt. Dabei entspricht die
Darstellung der Anzeige des PostgreSQL-Terminals.
l_orderkey | count
------------+------1 |
6
3 |
6
7 |
7
32 |
6
35 |
6
39 |
6
67 |
6
68 |
7
69 |
6
70 |
6
71 |
6
Abbildung 3.11: Teilmenge des Ergebnisses der Beispiel-Anfrage
Der vom „Parser” anhand dieser Anfrage erstellte Query-Tree ist in Abbildung 3.12
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
35
dargestellt. Dabei wurde der Baum aus Darstellungsgründen nach rechts gedreht. Somit befindet sich die Wurzel des Baumes auf der linken und die Blätter auf der rechten
Seite. Die Wurzel, Knoten und Blätter des Baumes sind Nodes, welche durch Rechtecke symbolisiert werden. Der Typ des Nodes ist der oberste Eintrag jedes Rechteckes.
Die weiteren Einträge sind die Variablen des Nodes, wobei sich jede Variable aus ihrem Namen und dem Wert zusammensetzt. Einige Variablen wurden bei jedem Node
weggelassen, denn eine ausführliche Liste der Variablen würde die Abbildung unübersichtlich machen und den Platz einer Seite überschreiten. Außerdem sind nicht alle
Nodes des Baumes abgebildet. Der Grund dafür ist derselbe wie bei den Variablen.
Dennoch ist der Query-Tree, soweit es zum Verständis beiträgt, vollständig.
Variablen vom Typ „Pointer” sind gesondert dargestellt. Ihr Wert steht nicht nach
einem Gleichheitszeichen, sondern erscheint in einem separatem Quadrat. Ist der Wert
des Zeigers gesetzt, so befindet sich in dem Quadrat ein schwarzer Kreis, welcher der
Ausgangspunkt für den Zeiger ist. Der Wert des Zeigers wird durch dem von dort
ausgehenden Pfeil symbolisiert. Ist der Wert des Zeigers nicht gesetzt, dies entspricht
dem Wert „Null”, so ist in dem Quadrat ein „x” abgebildet. Einige der Nodes sind in
einer Liste eingebettet, wie sie in Kapitel 3.5 beschrieben wurden. Aus Gründen der
Übersichtlichkeit wurde auf die Darstellung der Listenstruktur verzichtet. Stattdessen
haben Nodes, die sich in einer Liste befinden, eine zusätzliche Variable mit dem Namen „next”. Die kursive Schreibweise soll verdeutlichen, dass diese Variable nicht im
eigentlichen Sinne zu dem Node gehört. Die Variable „next” ist vom Typ „Pointer”
und verweist jeweils auf das nächste Element der Liste.
Der Query-Tree in Abbildung 3.12 besteht aus den folgenden Abschnitten:
• „Query-Node”
Dieser Node befindet sich am linken Rand der Abbildung und ist damit die Wurzel des Baumes. Zur besseren Übersicht ist der Query-Node in Abbildung 3.13
alleine dargestellt. Seine oberste Variable mit dem Namen „commandType” legt
den Typ der Anfrage fest. Möglich sind Select-, Insert-, Update-, Delete- und
Utility-Anfragen. Die ersten vier Typen sind selbsterklärend. Der fünfte Typ,
die Utility-Anfragen, wurden bereits in Kapitel 3.4 erwähnt. Zu diesem Typ gehören alle Anfragen, die keine aufwendige Planung und Ausführung durch das
Datenbanksystem benötigen. Solche Anfragen sind u.a. das Anlegen von Tabellen oder die Bereinigung der Datenbank.
Im Beispiel hat die Variable „commandType” den Wert 1. Dieser steht für eine Select-Anfrage. Alle anderen Variablen des Query-Node sind Zeiger. Diese
verweisen auf Nodes und Listen, welche Detailinformationen zur Anfrage enthalten.
• „Rangetable”
Der Rangetable enthält alle in der Anfrage verwendeten Tupel-Quellen. Solch
eine Quelle kann eine Relation der Datenbank und das Ergebnis einer Unteranfrage, Funktion oder „Joins” sein. Der Rangetable ist eine Liste, auf welche
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
36
Abbildung 3.12: Query-Tree der Beispiel-Anfrage
die Variable „rtable” des Query-Nodes verweist. In der Liste existiert für jede
verwendete Tupel-Quelle ein Eintrag. Die Daten jedes Eintrages werden in einem separaten RangeTableEntry-Node gespeichert. Dieser hat zwei Variablen.
Die erste mit dem Namen „relkind”, gibt die Art der Tupel-Quelle an. Mit der
zweiten Variablen wird die datenbankinterne „Id” der Relation angegeben. Aus
diesem Grund hat sie den Namen „relid”. Der Wert dieser Variablen ist nur von
Bedeutung, wenn die Tupel-Quelle auch eine Relation ist.
Der Rangetable der Beispiel-Anfrage ist in Abbildung 3.14 dargestellt. Vom
„Parser” wurden zwei Einträge im Rangetable angelegt. Einer entspricht
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
37
Abbildung 3.13: Query-Tree Ausschnitt - Query-Node
Abbildung 3.14: Query-Tree Ausschnitt - Rangetable
dabei der Tabelle „orders” und einer der Tabelle „lineitem”. Bei beiden
RangeTableEntry-Nodes hat die Variable „relkind” den Wert 0, welcher die
Tupel-Quellen als Relationen auszeichnet. Dies bedeutet gleichzeitig, dass die
„Id” jeder Relationen in der Variablen „relid” abgelegt wurde.
Der komplette Rangetable entspricht damit der Umsetzung des Textes „...FROM
orders, lineitem...” aus der Beispiel-Anfrage.
• „Jointree”
Zwei Teile der Anfrage sind im „Jointree” untergebracht. Der erste Teil gibt an,
in welcher Reihenfolge die verwendeten Relationen verbunden werden sollen.
Wird die Reihenfolge in der Anfrage nicht festgelegt, so ist sie dem Datenbanksystem überlassen. Dieser Fall trifft auch für die Beispiel-Anfrage zu. Der zweite
Teil des „Jointrees” sind die Selektionsbedingungen der Anfrage. Diese werden
in der Anfrage nach dem „WHERE”-Schlüsselwort angegeben.
Der komplette „Jointree” der Beispiel-Anfrage ist in Abbildung 3.15 dargestellt.
Dabei ist die Wurzel des „Jointrees” der FromExpr-Node. Auf ihn zeigt die Variable „jointree” des Query-Nodes. Die erste Variable des FromExpr-Nodes hat
den Namen „fromlist”. Sie enthält die gerade beschriebene Reihenfolge, in welcher die Relationen verbunden werden sollen. Im Beispiel erfolgt dies durch
eine Liste mit RangeTblRef-Nodes. Jeder dieser Nodes hat nur eine Variable.
Der Wert dieser Variablen gibt eine Position im Rangetable an. D.h., über diese
Variable wird ein Verweis in den Rangetable erzeugt. Auf diese Weise wird festgelegt, welche Tupel-Quellen miteinander verbunden werden sollen.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
38
Abbildung 3.15: Query-Tree Ausschnitt - Jointree
Das zweite Attribut des FromExpr-Node verweist auf die Selektionsbedingungen. Es trägt den Namen „quals”. Der Baum der Selektionsbedingungen beginnt
mit einem BoolExpr-Node. Dieser gibt die Verknüpfungsart der Bedingungen
an. Die Art ist in der Variablen „boolop” enthalten und im Beispiel mit dem
Wert „and” belegt. Das bedeutet, dass die einzelnen Bedingungen mit einem logischen „Und” verknüpft werden. Die zu verknüpfenden Bedingungen sind in
einer Liste abgelegt, auf welche die Variable „args” verweist.
Jeder Eintrag dieser Liste ist ein OpExpr-Node. Dieser legt den Operator fest, der
für die Auswertung der Bedingung genutzt werden soll. Die Variable „opno” gibt
die „Id” des Operators an. Im ersten OpExpr-Node der Liste ist der Wert der „Id”
96. Hinter dieser „Id” verbirgt sich der Gleichheits-Operator für Integer-Werte.
Dies ist der Vergleich „o_orderkey = l_orderkey” der Beispiel-Anfrage. Weitere
Details zu den Operatoren sind im Kapitel 3.9 zu finden. Die Variable „args”
des OpExpr-Nodes verweist auf weitere Informationen zu der Bedingung. Diese
können Konstanten und die Namen der Attribute sein, die zur Bedingung gehören.
Der zweite OpExpr-Node hat die „opno” 664. Damit steht er für den Datumsvergleich, also dem Ausdruck „o_orderdate < ’1998-06-20”’ der Beispiel-Anfrage.
• „Targetlist”
Die gewünschten Attribute der Ergebnis-Tupel werden in der Targetlist festgelegt. Sie entspricht damit dem Select-Teil der Anfrage, also dem Text „SELECT
l_orderkey, count(l_partkey)” aus der Beispiel-Anfrage. Auf die Targetlist verweist die Variable „targetList” des Query-Nodes. Die Targetlist der BeispielAnfrage ist in Abbildung 3.16 dargestellt. Jede Targetlist besteht aus einem oder
mehreren TargetEntry-Nodes. Dabei steht jeder Node für ein Attribut des SelectTeils. Die Variable „expr” des TargetEntry-Nodes verweist auf die Informationen
zu dem entsprechenden Attribut. Mittels der Variable „next” wird auf den nächs2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
39
Abbildung 3.16: Query-Tree Ausschnitt - Targetlist
ten Eintrag der Liste verwiesen.
Für die Beispiel-Anfrage existieren zwei Einträge in der Targetlist. Dabei ist der
zweite der einfachere von beiden und wird deswegen zuerst beschrieben. Dieses Attribut der Targetlist wird mittels eines Var-Nodes dargestellt. Dabei ist die
Aufgabe des Var-Nodes lediglich ein Verweis in den Rangetable, welcher bereits
beschrieben wurde. Die Variable „varno” des Var-Nodes gibt die Nummer des
Eintrages im Rangetable und damit die Tupel-Quelle an. Die zweite Variable
„varattno” bezieht sich dann auf diesen Eintrag und benennt die Nummer des
Attributes, welches ausgelesen werden soll. Im Beispiel wird das erste Attribut
des zweiten Rangetable-Eintrages angegeben. Damit beschreibt dieser Eintrag
die Relation „orders” und von dieser das Attribut „l_orderkey”.
Der erste Eintrag der Targetlist aus der Beispiel-Anfrage sieht etwas umfangreicher aus. Auch hier ist am Ende der Verweise, ganz rechts, wieder ein Var-Node
zu finden. Dieser verweist auf denselben Rangetable-Eintrag, allerdings auf das
zweite Attribut. Damit wird das Attribut „l_partkey” der Relation „orders” beschrieben. Vor dem Var-Node befindet sich noch ein AggRef-Node. Dieser beschreibt die Aggregations-Funktion, welche über diesem Attribut erfolgen soll.
Die Variable „aggfnoid” gibt dabei die zu verwendende Aggregations-Funktion
an, welche im Beispiel „count” ist und mit der „Id” „2147” beschrieben wird.
Damit entspricht dieser Eintrag der Targetlist dem Text „count(l_partkey)” aus
der Anfrage.
• „GroupClause”
Die Variable „groupClause” des Query-Nodes verweist auf die „GroupClause”.
Diese enthält alle Informationen zu verwendeten Gruppierungen der Anfrage,
welche mittels des SQL-Schlüsselwortes „GROUP BY” deklariert wurden. Dabei wird für jede Spalte, nach der gruppiert wird, ein eigener GroupClause-Node
angelegt. Dieser umfasst zwei Variablen. Die erste hat den Namen „tleSortGroupRef” und verweist auf die Targetlist. Der Wert der Variablen gibt die Nummer des Targetlist-Eintrages an, nach dem die Gruppierung erfolgt. Die Variable
„sortop” legt den Operator fest, welcher für die Durchführung der Gruppierung
verwendet werden soll.
Die „GroupClause” der Beispiel-Anfrage ist in der Abbildung 3.17 dargestellt.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
40
Abbildung 3.17: Query-Tree Ausschnitt - GroupClause
Abbildung 3.18: Query-Tree Ausschnitt - HavingQual
Sie besteht aus einem einzigen GroupClause-Node. Der Wert 2 der Variablen
„tleSortGroupRef” verweist auf den zweiten Eintrag der Targetlist, welches das
Attribut „l_orderkey” ist. Die Variable „sortop” hat den Wert 97, welches die
„Id” des Operators ist, der für die Gruppierung verwendet wird.
• „HavingQual”
Die letzte noch nicht beschriebene Variable des Query-Nodes hat den Namen
„havingQual”. Sie zeigt analog zu ihrem Namen auf die „HavingQual” der Anfrage. Diese gibt alle Bedingungen, die hinter dem „SQL-Having-Teil” stehen,
an.
Die „HavingQual” der Beispiel-Anfrage ist in Abbildung 3.18 dargestellt. Ihre
Wurzel ist ein OpExpr-Node, dessen Aufgabe der Vergleich zweier Werte ist.
Die Art des Vergleiches wird mittels der Variablen „opno” festgelegt. Ihr Wert
419 steht für „größergleich”. Die Variable „args” zeigt auf eine Liste, welche
die zu vergleichenden Operanden enthält. Der erste Operand ist ein AggRefNode zusammen mit einem Var-Node. Diese Kombination wurde bereits bei der
Targetlist beschrieben. Im Fall der „HavingQual” stehen diese zwei Nodes für
den Text „count(l_partkey)” der Beispiel-Anfrage. Der nächste Node der Liste
ist ein Const-Node. Dieser steht für einen konstanten Wert, mit dem verglichen
wird. Die Variable „constvalue” enthält dabei den konkreten Wert, welcher 5 ist.
Zusammen ergibt sich der Ausdruck „HAVING count(l_partkey) > 5”, welcher
durch die „HavingQual” dargestellt wird.
Durch die beschriebenen Teile des Query-Trees wird die gesamte Beispiel-Anfrage
aus Abbildung 3.10 repräsentiert. Der Query-Tree kann nun genutzt werden, um die
Anfrage zu verarbeiten. Trotz der einfach erscheinenden Anfrage ist ein komplexer
Query-Tree entstanden. Dabei stellt die Abbildung 3.12 nicht mal den vollständigen
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
41
Baum dar. Dies lässt erahnen, wie verzweigt ein Query-Tree bei hoch komplexen Anfragen werden kann.
3.6.3
Plan-Tree
Nach der Transformation der Anfrage in einen Query-Tree wird dieser Baum im
PostgreSQL-System weiterverarbeitet. Handelt es sich bei der Anfrage um eine komplexe Anfrage, wird der Query-Tree an den „Planner” übergeben. Die Aufgabe des
„Planners” ist die Erstellung des Plan-Trees. Dieser Baum wird für die weitere Verarbeitung der Anfrage benötigt.
Der Aufbau und die Funktion eines Plan-Trees soll in diesem Kapitel vorgestellt
werden. Dazu wird das gleiche Beispiel wie beim Query-Tree verwendet. Dieses ist in
Kapitel 3.6.2 beschrieben.
Der „Planner” hat als einzigen Input den Query-Tree. Aus diesem erstellt er den
Plan-Tree. Dieser Baum beschreibt die Arbeitsschritte des Datenbanksystems, die notwendig sind, um die Anfrage zu beantworten. Wie der Query-Tree setzt sich auch
der Plan-Tree aus Nodes zusammen. Allerdings stammen die verwendeten Nodes aus
einer anderen Kategorie (siehe Kapitel 3.6.1). Der „Planner” nutzt Nodes aus der Kategorie Plannodes. Deren Besonderheit sind die Variablen „startup_cost”, „total_cost”,
„lefttree” und „righttree”. Die beiden ersten Variablen dienen der Speicherung von errechneten Kosten. Diese Kosten sind eine Schätzung des „Planners” und dienen als
Maß für den notwendigen Aufwand, um die Ergebnismenge eines Teilbaumes zu bilden. Die Variablen „lefttree” und „righttree” sind vom Typ „Pointer”. Sie verweisen
auf weitere Nodes und ermöglichen so den Aufbau des Plan-Trees.
Der Plan-Tree des Beispiels ist in Abbildung 3.19 dargestellt. Er besteht im
Einzelnen aus:
• „Agg-Node”
Der Agg-Node ist die Wurzel des gesamten Plan-Trees. Bei der späteren Verarbeitung durch den „Executor” bedeutet dies, er wird als letzter Node verarbeitet.
Der Grund dafür liegt in der rekursiven Verarbeitung des „Executors”.
Die Aufgabe des Agg-Nodes ist die Repräsentation einer Aggregation. Dies entspricht dem Text „count(l_partkey)” der Beispiel-Anfrage. Der Agg-Node des
Beispiel-Plan-Trees ist in Abbildung 3.20 dargestellt. Die ersten zwei Variablen
des Nodes stellen die bereits beschriebene Kostenschätzung für den gesamten
Baum dar. Dabei gibt „startup_cost” die Kosten an, welche bei der Verarbeitung
mindestens benötigt werden, auch wenn die Ergebnismenge leer ist. Mit „total_cost” werden die Kosten bezeichnet, welche entstehen, wenn alle Tupel der
beteiligten Relationen in der Ergebnismenge sind. Die Variable „aggstrategy”
legt den verwendeten Gruppierungstyp fest. Dabei ist eine „reine Aggregation”
oder „gruppierte Aggregation” möglich. Bei der „gruppierten Aggregation” wird
die Aggregation auf die jeweiligen Gruppen angewendet. Liegen die Input-Tupel
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
42
Abbildung 3.19: Plan-Tree der Beispiel-Anfrage
des Agg-Nodes nicht sortiert nach dem Gruppierungs-Attribut vor, wird zur Bildung der Gruppen ein Hash benutzt. Ansonsten reicht die Sortierung aus. Der
im Beispiel verwendete Wert 1 der Variablen „aggstrategy” steht für „gruppierte
Aggregation” mit bereits sortierten Input-Tupeln.
Wie bereits beschrieben, wird über die Variablen „lefttree” und „righttree” der
Plan-Tree aufgebaut. Diese beiden Variablen zeigen auf weitere Nodes, welche die Input-Tupel für den Node erstellen. Im Fall des Agg-Nodes ist nur eine
Menge von Input-Tupel” zulässig. Aus diesem Grund ist die Variable „righttree”
nicht gesetzt. Lediglich die Variable „lefttree” verweist auf einen Node.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
43
Abbildung 3.20: Plan-Tree Ausschnitt - Agg-Node
Die Variablen des Agg-Nodes reichen nicht aus, um die Funktionalität des Nodes vollständig zu beschreiben. So enthält die Variable „targetlist” einen Zeiger
auf einen „Unter-Baum”. Die Nodes des „Unter-Baumes” sind in der Abbildung
3.20 grau dargestellt. Die Targetlist im Plan-Tree hat die gleiche Funktion wie
schon im Query-Tree. Diese ist die Beschreibung der Attribute, welche die Ergebnismenge des Nodes bilden. Allerdings ist die Bedeutung der Var-Nodes im
Plan-Tree anders. Sie sind zwar auch Referenzen auf Attribute, allerdings verweisen sie nicht auf einen Rangetable wie im Query-Tree. Die Variable „varno”
hat bei beiden Var-Nodes den Wert 0, da es nur eine Tupel-Quelle gibt und dies
ist der „lefttree”. Die Variable „varattno” enthält die Nummer eines Attributes
innerhalb dieser Tupel-Quelle. Eine detaillierte Beschreibung aller Nodes der
Targetlist ist in Kapitel 3.6.2 zu finden.
• „MergeJoin-Node”
Dieser Node steht für einen „MergeJoin”. Dabei werden die Tupel zweier Ergebnismengen zu einer verbunden. Die Beschreibung des „MergeJoin-Algorithmus”
würde an dieser Stelle zu weit führen und ist in der Fachliteratur nachzulesen. Voraussetzung für einen „MergeJoin” ist, dass die zu vereinigenden TupelMengen sortiert sind und zwar jeweils nach dem Attribut, das zum „Join” ver2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
44
wendet wird. Dies sind im Beispiel die Attribute „o_orderkey” und „l_orderkey”.
Aus den sortierten Input-Tupel und dem Algorithmus des „MergeJoin” resultiert, dass die Ergebnismenge des MergeJoin-Node ebenfalls wieder sortiert ist
und zwar nach den beiden Join-Attributen. Damit wird auch im Beispiel die Voraussetzung des Agg-Nodes geschaffen, welche bereits beschrieben wurde.
Der MergeJoin-Node ist zusammen mit seiner Targetlist und seinen „Mer-
Abbildung 3.21: Plan-Tree Ausschnitt - MergeJoin-Node
geclauses” in der Abbildung 3.21 dargestellt. Die ersten zwei Variablen des
MergeJoin-Nodes sind die vom „Planner” geschätzten Kosten für den Teilbaum.
Mit „lefttree” und „righttree” wird auf die Input-Tupel verwiesen und die Variable „targetlist” zeigt auf die Targetlist des MergeJoin-Nodes. Alle diese Variablen wurden bereits ausführlich beschrieben. Neu an der Targetlist sind lediglich
die recht hohen Werte der Variablen „varno” in den Var-Nodes. Deren Bedeutung wird beim Sort-Node noch beschrieben.
Durch die Variable „mergeclauses” wird angegeben, wie die zwei Ergebnismengen verknüpft werden. Dafür zeigt diese Variable auf einen Baum, welcher in
der Abbildung 3.21 grau dargestellt ist. Oberster Node dieses Baumes ist ein
OpExpr-Node. Dieser Node-Typ wurde bereits beim Query-Tree beschrieben.
Der OpExpr-Node repräsentiert in diesem Fall die Verknüpfungsart „Gleichheit”. Dabei werden die Attribute, welche durch die Var-Nodes beschrieben werden, auf ihre „Gleichheit” überprüft. Die Var-Nodes verweisen, wie schon beim
Agg-Node, auf die Ergebnismengen des „lefttrees” und „righttrees”. Hat die Variable „varno” einen Wert von 65001, steht dieser für den „lefttree”. Das bedeutet
im Beispiel, dass der erste Var-Node auf das erste Attribut aller Tupel aus dem
„lefttree” verweist. Der zweite Var-Node, mit einem Wert von 65000 der Varia2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
45
blen „varno” verweist auf den „righttree”. Dabei wird ebenfalls das erste Attribut
angegeben.
• „Sort-Node”
Eine Grundvoraussetzung des „MergeJoins” ist die Sortierung der zwei TupelMengen, die verbunden werden sollen. Aus diesem Grund beginnt jeder der Teilbäume des MergeJoin-Nodes („lefttree” und „righttree”) mit einem Sort-Node.
Da beide Teilbäume fast identisch sind, wird im Weiteren nur der „lefttree” des
MergeJoin-Nodes beschrieben.
Der Sort-Node ist in der Abbildung 3.22 dargestellt. Er hat, wie auch schon der
Abbildung 3.22: Plan-Tree Ausschnitt - Sort-Node
Agg-Node, nur eine Input-Menge von Tupeln. Auf diese verweist die Variable
„lefttree”. Die „targetlist” des Nodes erfüllt ebenfalls den bereits beschriebenen
Zweck. Aus diesem Grund wurde sie in der Abbildung nur durch drei Punkte
angedeutet.
Die obersten zwei Variablen des Nodes geben die geschätzten Kosten des Teilbaumes an. Die Variable „sortColIdx” bezieht sich auf alle Input-Tupel. Ihr Wert
nennt die Nummer des Attributes, nach dem die Sortierung erfolgen soll. Im
Beispiel nach dem ersten Attribut. Dabei wird zur Sortierung der Operator verwendet, der mittels der Variable „sortOperators” angegeben wurde. Der Wert 97
steht für einen Kleiner-Vergleich mit Integer-Werten. Ausführlichere Informationen zu den Operatoren in PostgreSQL sind im Kapitel 3.9.2 zu finden.
• „SeqScan-Node”
Dieser Node ist als Blatt des Plan-Trees zu verstehen. Demnach wird ihn der
„Executor” als erstes verarbeiten. Der SeqScan-Node ist in Abbildung 3.23 dargestellt. Er steht für das vollständige Durchsuchen einer Tabelle. Dies wird als
„sequentieller Scan” bezeichnet. Damit ist der Node eine mögliche Ausgangsquelle von Tupeln. Auch dieser Node hat die Variablen „startup_cost”, „total_cost”, „lefttree”, „righttree” und „targetlist”, welche bereits beschrieben wurden.
Die verbleibende Variable „qual” gibt die Bedingungen an, nach denen die Tabelle durchsucht wird. Nur die Tupel, die alle Bedingungen erfüllen, sind auch
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
46
Abbildung 3.23: Plan-Tree Ausschnitt - SeqScan-Node
in der Ergebnismenge des SeqScan-Nodes. Die Bedingungen werden mittels eines Baumes ausgedrückt, welcher in der Abbildung 3.23 grau dargestellt ist.
Oberster Node ist ein OpExpr-Node. Er steht für den Kleiner-Vergleich von
„Text”. Der erste zu vergleichende Wert ist ein FuncExpr-Node. Über seine
Variable „funcid” wird die Funktion angegeben, welche er ausführen soll. Der
Wert von 749 steht für die Umwandlung eines Datums in Text. Das Attribut
für die Umwandlung wird durch den Var-Node angegeben, auf den die Variable
„args” des FuncExpr-Node verweist. Im Beispiel entspricht dies dem Attribut
„o_orderdate”. Der zweite Wert für den Vergleich des OpExpr-Nodes ist der
Const-Node. Er enthält nur einen Text. Damit entspricht der ganze Teilbaum
dem Ausdruck „o_orderdate < ’1998-06-20”’ der Beispiel-Anfrage.
Anhand dieses Plan-Trees ist nun der „Executor” in der Lage, die Ergebnismenge der
Anfrage zu bilden. Wie anhand des Beispiels erkennbar war, ist die Struktur des PlanTrees völlig verschieden zum Query-Tree, auch wenn beide die gleiche Anfrage repräsentieren.
3.7
CommandCounter
Der „CommandCounter” ist eine wichtige Variable für die Transaktionen. Er wird für
jede Transaktion separat angelegt und zu Beginn der Transaktion auf Null gesetzt.
Mittels der Funktion „CommandCounterIncrement” kann er systemintern um jeweils
einen Zählerschritt erhöht werden.
Normalerweise sind von der Transaktion gemachte Änderungen nicht vor ihrem
Abschluss sichtbar. Dies macht Änderungen in der Form „x = x + 1” erst möglich.
Trotzdem kann es sein, dass Datenbanksystem-Vorgänge eine Einsicht in ihre Änderungen benötigen. Ist dies der Fall, hilft die Funktion „CommandCounterIncrement”.
Jede Erhöhung des „CommandCounters” macht die bisherigen Änderungen für die
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
47
Transaktion sichtbar, verbirgt aber kommende. Die Funktion „CommandCounterIncrement” wird in verschiedenen Teilen des PostgreSQL-Datenbanksystems eingesetzt,
so auch auch im Index-Advisor.
Zum besseren Verständnis wird die Nutzung des „CommandCounters” an einem
Beispiel erklärt. Die Transaktion des Beispiels hat die Aufgabe, an einer Relation Änderungen vorzunehmen und danach einen Durchschnittswert zu bilden. Der Ablauf
dieser Transaktion ist in Abbildung 3.24 dargestellt. Dabei beginnt die Verarbeitung
der Transaktion ganz links.
Abbildung 3.24: CommandCounter-Beispiel
Die erste Aktion der Transaktion ist die Durchführung eines SQL-Updates. Dabei
erhöht die Transaktion bei einem Tupel den Wert des Attributes „att2” um drei. Diese
Funktion entspricht damit der Gleichung „x = x + 3”. Diese Änderung bleibt aber für
die Transaktion selbst unsichtbar und ist deswegen in der Abbildung 3.24 nur grau
unterlegt.
Im zweiten Schritt führt die Transaktion ein SQL-Insert durch. Dabei wird ein
neues Tupel mit den Werten „rtf” und 52 in die Relation eingefügt. Auch dieses Tupel
ist für die Transaktion zunächst nicht sichtbar und deswegen nur grau angedeutet.
Der dritte Schritt der Transaktion ist die Bildung eines Mittelwertes über das Attribut „att2”. Die Mittelwertbildung ist jederzeit möglich, allerdings würden die gerade
erfolgten Änderungen nicht mit einbezogen werden. Aus diesem Grund muss vorher
ein Aufruf von „CommandCounterIncrement” erfolgen. Danach sind der neue Attributwert (90) und das neue Tupel („rtf”,52) für die Transaktion sichtbar. Mittels eines
SQL-Select kann nun der korrekte Mittelwert ausgelesen werden, und weitere Transaktionsabschnitte können folgen.
Durch den „CommandCounter” kann jede Transaktion selbst entscheiden, ob sie
ihre Änderungen sehen möchte oder nicht. Dies erhöht die Möglichkeiten von Transaktionen enorm, macht aber gleichzeitig eine Gliederung in Abschnitte notwendig.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
3.8
48
Disk-Pages
Zu jeder Relation in einer PostgreSQL-Datenbank existiert eine eigene Datei. In
dieser werden die Tupel der Relation abgelegt. Die Datei wird vom PostgreSQLDatenbanksystem in sogenannte Disk-Pages aufgeteilt. Diese haben eine feste Größe
und Struktur. Ihre Aufgabe besteht in der Speicherung von Tupeln. Dabei bieten sie
folgende Vorteile:
• einfaches Löschen von Tupeln
Beim Entfernen eines Tupels aus der Relation und damit aus der zugehörigen
Datei, muss nicht die gesamte Datei umgeordnet werden. Die entstandene Lücke
wird von der Disk-Page verwaltet und kann für neue Tupel verwendet werden.
• gezieltes Auslesen einer Tupelmenge
Beim Durchsuchen einer Relation muss nicht die ganze Datei der Relation eingelesen werden. Vielmehr ließt das PostgreSQL-Systems Disk-Page für Disk-Page
ein. Wird die gesuchte Information gefunden, kann die Suche sofort beendet
werden. Auch ist das Auslesen einer beliebigen Disk-Page möglich. Dies wird
angewendet, wenn die Nummer der Disk-Page bereits bekannt ist, zum Beispiel
durch einen Index.
• einfache Umsortierung von Tupeln
Die Disk-Page-Struktur macht es möglich, die Tupel einer Disk-Page zu sortieren, ohne die Tupel-Daten verschieben zu müssen. Bei der Sortierung werden
lediglich Zeiger geändert. Diese haben ein viel kleineres Speichervolumen, als
die Tupel selbst. Somit kann die Sortierung sehr ressourcen-schonend erfolgen.
Die Struktur einer Disk-Page ist in Abbildung 3.25 dargestellt. Jede Disk-Page beginnt
mit einem „Pageheader”, dessen Inhalt und Größe fest vorgeschrieben sind. Im „Pageheader” sind Informationen wie die Version der Diskpage oder Offset-Werte innerhalb der Page gespeichert. Weitere Disk-Page-Informationen, für die kein Platz mehr
im „Pageheader” ist, werden im „Special Space” abgelegt. Dabei wird die Größe des
„Special Spaces” im „Pageheader” festgelegt und ist dadurch dynamisch. Genutzt wird
der „Special Space” zum Beispiel von „Indizes”, welche dort Zeiger auf andere DiskPages ablegen. Durch diese Zeiger bauen sie ihre Speicherstrukturen, wie zum Beispiel
Bäume, auf.
Der „Pageheader” und „Special Space” sind Speicherplatz, der bei jeder DiskPage nicht von Tupel genutzt werden kann. Aus diesem Grund werden diese Daten
als „Overhead” bezeichnet und sind in der Abbildung 3.25 weiß dargestellt.
An den „Pageheader” schließen sich die „Itempointer” an. Diese wurden in der
Abbildung mit „Itemp1”, „Itemp2” und „Itemp3” bezeichnet. Für jedes Tupel in der
Disk-Page gibt es einen „Itempointer”. Dieser verweist auf die Speicherposition der
Tupel-Daten. Die Verwendung von Zeiger ist notwendig, denn die Größe der Tupel
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
49
ist nicht vorgeschrieben. So kann es sein, dass eine Disk-Page 1000 oder nur 10 Tupel enthält. Eine feste Speicherplatz-Aufteilung für jedes Tupel der Disk-Page ist so
nicht möglich. Die „Itempointer” verweisen jeweils auf den Anfang eines Tupels und
ermöglichen so beliebige Tupel-Größen.
Abbildung 3.25: Struktur einer Disk-Page
Ein weiterer Vorteil der „Itempointer” wurde bereits beschrieben. Dies ist die einfache Sortierung von Tupeln. Durch die Änderung der Reihenfolge der „Itempointer”
kann eine Disk-Page sortiert werden, ohne dass die eigentlichen Tupel-Daten verschoben werden. Da ein „Itempointer” für jedes Tupel benötigt wird, er selbst aber keine
Informationen des Tupels speichert, gehört er zum Tupel-Overhead.
Innerhalb einer Disk-Page werden neue „Itempointer” immer nach dem „Pageheader” oder dem letzten „Itempointer” angelegt. In der Abbildung 3.25 bedeutet dies, die
„Itempointer” „wachsen” von links nach rechts und von oben nach unten. Die Daten
der Tupel hingegen werden genau umgekehrt angelegt. Sie beginnen vor dem „Special Spache” und „wachsen” von rechts nach links und von unten nach oben. Damit
„wachsen” die „Itempointer” und Tupel-Daten aufeinander zu. Dies ermöglicht es,
viele „Itempointer” mit jeweis kleinen Tupel-Daten, als auch wenige „Itempointer”
mit jeweils umfangreichen Tupel-Daten anzulegen.
Die Daten der Tupel sind in Abbildung 3.25 mit „Tupel1”, „Tupel2” und „Tupel3”
bezeichnet. Ihre Nummerierung korreliert mit denen der „Itempointer”. Zwischen den
Tupel-Daten und den „Itempointern” liegt der ungenutzte Bereich der Disk-Page. Erst
wenn dieser so gering ist, dass er nicht mehr einen „Itempointer” und die dazugehörigen Tupel-Daten speichern kann, ist die Disk-Page vollständig gefüllt.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
3.9
50
System Catalog
Außer den in den Datenbanken gespeicherten Informationen, benötigt das Datenbanksystem zur Anfrageverarbeitung weitere Informationen. Diese werden als Meta-Daten
bezeichnet und sind z.B. Informationen über die Tabellen und deren Spalten. Die MetaDaten geben des Weiteren Aufschluss über die angelegten Datenbanken und wie auf
diese zugegriffen werden kann.
Die Speicherung der Meta-Daten erfolgt in PostgreSQL im sogenannten SystemCatalog. Bei Veränderungen an einer Datenbank wird der System-Catalog automatisch
mit angepasst. Dies ist z.B. der Fall beim Anlegen oder Modifizieren einer Tabelle.
Des Weiteren besteht die Möglichkeit, direkt auf den System-Catalog zuzugreifen
und Änderungen an ihm durchzuführen. Auf diese Weise ist es möglich, PostgreSQL
auf einem einfachen Weg um neue Fähigkeiten zu erweitern [11].
In den folgenden Unterkapiteln werden einige Teile des System-Catalogs vorgestellt. Dies sind Teile, welche für den Index-Advisor eine besondere Bedeutung haben
und von ihm genutzt werden.
3.9.1
Access Methods
Die Access-Methods enthalten alle möglichen Index-Zugriffs-Methoden auf eine Relation, welche das Datenbanksystem beherrscht. Wie bereits erwähnt können die Funktionen des Datenbanksystems erweitert werden. Neue Index-Zugriffs-Methoden müssen in diesen Teil des System-Catalogs eingetragen werden, erst dann können sie genutzt werden.
Die Access-Methods werden in der Tabelle mit dem Namen „pg_am” abgelegt.
Beispiele für Zugriffs-Methoden sind „R-Tree” oder „B-Tree”. Diese Index-Typen
wurden in Kapitel 3.1.3 bereits näher beschrieben. Für jeden Eintrag in „pg_am”, also
für jede Index-Zugriffs-Methode, werden die folgenden Informationen gespeichert:
• Name
Dies ist der Name der Index-Zugriffsmethode.
• Zugriffsstrategien
Mittels der „Zugriffsstrategien” wird festgelegt, zur Auswertung welcher Operationen ein Index in der Lage ist. Dies können zum Beispiel Vergleichsoperationen wie „größer als” („>”), „gleich („=”) oder „kleiner gleich” („<=”) sein.
• Sortierungsstrategien
Nicht jeder Index unterstützt einen sortierten Zugriff auf eine Relation. Ob ein
Index dies unterstützt und welche Sortierungen dies sind, ist in diesem Eintrag
vermerkt. Eine mögliche Sortierung ist zum Beispiel „aufsteigend”.
• Indexfunktionen
Das Datenbanksystem benötigt für die Nutzung eines Index und damit einer
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
51
Access-Method verschiedene Funktionen. So wird z.B. eine Funktion für den
Aufbau des Index benötigt. Die Namen der Funktionen werden in diesen Einträgen abgelegt.
• weitere Informationen
Zu den bereits beschriebenen Informationen werden noch weitere zu jeder
Access-Method abgelegt. Diese sind aber im Rahmen des Index-Advisors nicht
von Belang und können in der PostgreSQL-Dokumentation nachgelesen werden
[12].
3.9.2
Operatoren
Die Tabelle „pg_operator” enthält die Operatoren des PostgreSQL-Systems. Die Operatoren werden bei jeder Verarbeitung von Attributen benötigt. Das Prinzip der Operatoren ist in Abbildung 3.26 dargestellt. Der Input des Operators sind ein oder zwei
Werte, welche mit „Input A” und „Input B” bezeichnet wurden. Auf diese Werte wird
der Operator angewendet. Das Resultat ist der mit „Ergebnis” bezeichnete Wert. Beispiele für Operatoren sind die Addition („+”), Subtraktion („-”) und der Vergleich
(„=”).
Abbildung 3.26: Prinzip der PostgreSQL-Operatoren
Die Operatoren unterscheiden sich nicht nur durch die Berechnung, die sie durchführen, sondern auch durch die unterstützten Typen. Mit Typen ist die Art der Werte
„Input A”, „Input B” und „Ergebnis” gemeint. Solche Typen können z.B. Integer, Float
oder String sein. Auch ist es möglich, dass ein Operator überladen ist. Das bedeutet,
dieser Operator unterstützt mehrere Typen.
Ein Operator wird immer dann genutzt, wenn Attribute verarbeitet werden müssen.
Dies kann z.B. bei einer mathematischen Berechnung, der Suche nach einem Wert
oder der Negation eines Wertes sein. Der Operator erledigt diese Aufgabe, und das
Datenbanksystem arbeitet mit seinem Ergebnis weiter.
Zu jedem Operator werden die folgenden Informationen gespeichert:
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
52
• Name
Der Name des Operators.
• Operator-Art
Im Text der Anfrage werden Operatoren direkt verwendet, z.B. durch die Benutzung eines Gleichheitszeichens. Dabei unterscheidet PostgreSQL zwischen drei
Operatoren-Arten. Die erste ist „infix”. Bei dieser Art von Operatoren steht der
Operator zwischen den zwei Input-Werten, was der Fall bei dem Gleichheitszeichen ist. Die zwei weiteren Arten sind „prefix-” und „postfix-Operatoren”.
Beide haben nur einen Input-Wert. Dabei stehen „prefix-Operatoren” vor dem
Input-Wert und die „postfix-Operatoren” nach dem Input-Wert.
• Typ des Ergebnis
Der Typ des Ergebnisses hängt von dem verwendeten Operator ab. Während bei
einer Addition i.A. der Ergebnis-Typ mit dem Input-Typ übereinstimmt, ist das
Ergebnis bei einem Vergleich meist vom Typ „Boolean”.
• Funktion
Hinter der „Funktion” verbirgt sich die Art der Berechnung, welche der Operator
realisiert.
• weitere Informationen
Zu den bereits beschriebenen Informationen werden noch weitere zu jedem Operator abgelegt. Diese sind aber im Rahmen des Index-Advisors nicht von Belang
und können in der PostgreSQL-Dokumentation nachgelesen werden [13].
3.9.3
Operator Class
Eine Operator-Class gehört zu einer Access-Method und einem Attribut-Typ. Die
Operator-Class legt das Verhalten eines Index bei dem angegebenen Attribut-Typ fest.
Dies ist an einem Beispiel am verständlichsten.
Eine komplexe Zahl besteht aus einem Real- und Imaginär-Teil. Bei PostgreSQL
kann ein Attribut als komplexe Zahl definiert sein. Wird dieses Attribut indiziert, so
wird eine Operator-Class für komplexe Zahlen benötigt. Die Operator-Class ist dann
u.a. für die Auswertung und Sortierung der Attribut-Werte zuständig. Dazu enthält die
Operator-Class die Informationen, welche Operatoren und Funktionen für die einzelnen Aufgaben genutzt werden sollen.
Für eine Kombination von Attribut-Typ und Access-Method können auch mehrere
Operator-Classes definiert sein. Der Unterschied zwischen ihnen liegt dann in dem
jeweiligen Verhalten der Operator-Class. Bei einem komplexen Typ z.B. kann zum
Vergleich nur der Real- oder Imaginär-Teil des Wertes benutzt werden.
Wird beim Anlegen eines Index keine Operator-Class angegeben, so nutzt das Datenbanksystem die „Default Class”. Die Daten jeder Operator-Class werden in der Tabelle „pg_opclass” abgelegt. Jeder Eintrag enthält die folgenden Informationen:
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
53
• Name
Der Name der Operator-Class.
• Typ
Dies ist der Attribut-Typ, den die Operator-Class unterstützt.
• Access-Method
Dieser Eintrag legt fest, welche Access-Method die Operator-Class unterstützt.
• Default
Wenn es mehrere Operator-Class für eine Kombination von Attribut-Typ und
Access-Method gibt, so muss eine als „Default” deklariert werden. Dies geschieht mit diesem Eintrag.
• weitere Informationen
Zu den bereits beschriebenen Informationen werden noch weitere zu jeder
Operator-Class abgelegt. Diese sind aber im Rahmen des Index-Advisors nicht
von Belang und können in der PostgreSQL-Dokumentation nachgelesen werden
[14].
3.10
Run-time Configuration
Wie fast jede Software verfügt auch PostgreSQL über eine umfangreiche Konfiguration. Diese wird als „Run-time Configuration” bezeichnet. Sie legt vielfältige Einstellungen des Systems fest. Einige davon sind Konstanten, die Anzeige von DebuggingInformationen oder interne Funktionen wie die Arbeitsweise des „Planners”.
Die Parameter der „Run-time Configuration” können vom Typ „boolean”, „integer”, „float point” oder „string” sein. Jeder der Parameter hat einen Default-Wert, welcher zur „normalen” Arbeit des Datenbanksystems genügt. Die Parameter können aber
auch angepasst werden, um ein spezielles Verhalten zu erreichen.
Zur Änderung der Parameter stehen mehrere Möglichkeiten zur Verfügung. Die
erste Möglichkeit ist die Änderung der Datei „postgresql.conf” im Datenverzeichnis.
Diese Datei wird bei jedem Start des Datenbanksystems eingelesen und die Parameter entsprechend gesetzt. Ein Ausschnitt der Datei „postgresql.conf” ist in Abbildung
3.27 dargestellt. Die abgebildeten Parameter sind „enable_indexadvisor” und „enable_indexadvisor_terminaloutput”. Sie gehören zum Index-Advisor. Eine Beschreibung der Parameter ist in Kapitel 4.8 zu finden.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
#
#
#
#
#
#
#
#
54
----------------------------PostgreSQL configuration file
----------------------------This file consists of lines of the form:
name = value
#------------------------------# INDEXADVISOR
#------------------------------enable_indexadvisor = true
enable_indexadvisor_terminaloutput = false
Abbildung 3.27: Ausschnitt der Datei „postgresql.conf”
Die nächste Möglichkeit der Parameteränderung ist die Nutzung von „Command
line option”. Dabei wird der Datenbank der Wert von einem oder mehreren Parametern
übergeben. Dies kann nur zum Start des Datenbanksystems erfolgen und konkurriert zu
den Werten in der „postgresql.conf”. Die Regelung dabei ist, dass die Werte der „Command line” die höhere Priorität haben. Ein Beispiel der „Command line”-Nutzung ist
in Abbildung 3.28 dargestellt. Dabei wird der Parameter „enable_indexadvisor” auf
„false” gesetzt.
Abbildung 3.28: Parameter als „Command line option”
Die dritte Möglichkeit der Festlegung von Parameterwerten ist die Bindung an
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
55
einen User oder eine Datenbank. Dies geschieht mittels des SQL-Kommandos „ALTER USER” bzw. „ALTER DATABASE”. So kann das Verhalten des Datenbanksystems von Datenbank zu Datenbank und in Abhängigkeit des Benutzers unterschiedlich
sein. Die mit einem Nutzer oder einer Datenbank verbundenen Werte überschreiben
alle bisher genannten Arten. In Abbildung 3.29 ist das Setzen des Parameters „enable_indexadvisor” auf „false” dargestellt. Dabei wird dieser Wert dem Nutzer mit dem
Namen „benutzer” zugewiesen.
Abbildung 3.29: Parameter mit Benutzer verbinden
Vierte und letzte Art der Parameteränderung ist die Nutzung des „SET”Kommandos. Dieses kann jederzeit in einem Terminal benutzt werden. Es erlaubt die
beliebige Manipulation der Parameter. Ein mit „SET” angegebener Wert überschreibt
alle anderen Einstellungen. Die Nutzung des „SET”-Kommandos ist in Abbildung 3.30
dargestellt. Auch in diesem Beispiel wird wieder der Parameter „enable_indexadvisor”
auf „false” gesetzt. Das Datenbanksystem bestätigt dies mit der Rückmeldung „SET”.
2004-09-30/101/IN99/2254
KAPITEL 3. POSTGRESQL
56
Abbildung 3.30: Parameter mit „SET” setzen
Die Auswahl, auf welche Art ein Parameter geändert wird, unterliegt dem Benutzer. Dabei kann nicht bei jedem Parameter jede Möglichkeit angewendet werden. Dies
hängt von dem konkreten Parameter und seiner Bedeutung ab.
Mittels des Kommandos „SHOW” kann jederzeit der aktuelle Wert eines Parameters ermittelt werden. Die Nutzung des Befehls „SHOW” ist in Abbildung 3.31
dargestellt. Dabei wird der Wert des Parameters „enable_indexadvisor” abgefragt. Die
Antwort des Datenbanksystems zeigt, dass der Wert des Parameters „off” ist, welches
dem Wert „false” entspricht [15].
Abbildung 3.31: Parameter mit „SHOW” anzeigen
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
57
Kapitel 4
Index-Advisor
Die Aufgabe des Index-Advisors ist die Ermittlung von nützlichen Indizes. Dies soll
für jede Anfrage erfolgen, welche an das Datenbanksystem gestellt wird. Das bedeutet,
der Index-Advisor arbeitet synchron mit dem Datenbanksystem und ist ein Teil der
Verarbeitungs-Pipeline. Somit beeinflusst er die Zeit, welche benötigt wird, um eine
Anfrage zu beantworten.
Nur wenn der „Advisor” nicht zu viel Zeit für seine Aufgabe beansprucht, kann er
dem Datenbanksystem mehr Performance bringen. Ansonsten würde der Geschwindigkeitsvorteil durch neu angelegte Indizes, von dem Mehraufwand für den „Advisor”
wieder verbraucht werden und das Datenbanksystem sogar langsamer werden.
Für eine Anfrage gibt es meist nur eine kleine Anzahl von Indizes, die für sie von
Nutzen sind. Selbst wenn die Anfrage viele Attribute enthält, können Indizes nur bei
wenigen Attributen angewendet werden. Der Grund dafür wird bei der schrittweisen
Betrachtung der Anfrageverarbeitung klar.
Die Verarbeitung beginnt immer mit den Tabellen der Datenbank. Aus den Tabellen
werden die Tupel ausgelesen, welche für die Anfrage zutreffen. Danach befinden sich
die Tupel in temporären Tabellen, wo sie weiterverarbeitet werden. Dieser Ablauf ist
in Abbildung 4.1 dargestellt. Je nach Komplexität der Anfrage kann die Verarbeitung
der temporären Tabellen viel länger sein als das Auslesen der „normalen” Tabellen.
Mögliche Operationen dabei sind z.B. „Joins”, Sortierungen und Selektionen.
Bei allen Verarbeitungsschritten können Indizes nur beim Auslesen der „normalen” Tabellen genutzt werden. Denn nur für diese Tabellen wurden Indizes angelegt.
Temporäre Tabellen existieren ausschließlich während der Verarbeitung und können
höchstens vom Datenbanksystem indiziert werden. Dies ist der Grund, weshalb der
Index-Advisor ein Regelwerk benutzt. Dieses reduziert die Anzahl der betrachteten
Attribute, indem die Attribute entfernt werden, auf denen kein Index genutzt werden
kann. Diese Reduzierung erhöht die Performance des Index-Advisors ohne das Ergebnis zu verändern.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
58
Abbildung 4.1: genutzte Tabellen bei der Anfrageverarbeitung
4.1
Grundlegender Ablauf
Der Index-Advisor wird bei jeder komplexen Anfrage aufgerufen. Sein „Input” ist
die Anfrage selbst, in Form des Query-Trees. Dieser wurde bereits im Kapitel 3.6.2
beschrieben. Für die Anfrage soll der „Advisor” nützliche Indizes bestimmen und den
möglichen Performance-Gewinn dieser Indizes abschätzen. Die dabei durchlaufenen
Arbeitsschritte werden im Folgenden kurz beschrieben und sind in der Abbildung 4.2
grafisch dargestellt.
Für seine Arbeit nutzt der Index-Advisor den „Planner” des PostgreSQLDatenbanksystems. Der „Planner” wird dabei zweimal aufgerufen. Beim ersten Aufruf
ist die Datenbank unverändert. Der erstellte Plan-Tree (siehe Kapitel 3.6.3) wird lediglich als Vergleich benötigt und ist in der Abbildung 4.2 mit „Plan-Tree1” bezeichnet.
Im nächsten Schritt ermittelt der Index-Advisor die Index-Kandidaten. Dies ist eine
Liste der Attribute, die in der Anfrage genutzt werden und noch nicht indiziert sind.
Zur Ermittlung nutzt der „Advisor” den Query-Tree, welcher alle Informationen der
Anfrage enthält.
Stehen die Index-Kandidaten fest, wird für jedes der Attribute in der Liste ein Index angelegt. Diese Indizes sind allerdings nur „virtuell”, d.h. sie täuschen einen Index vor. Das Anlegen eines „realen” Index würde zu lange dauern und dadurch den
Performance-Gewinn des Index-Advisors verbauchen.
Nach dem Anlegen der „virtuellen Indizes” erfolgt der zweite Aufruf des „Planners”. Der generierte Plan-Tree ist in der Abbildung 4.2 mit „Plan-Tree2” bezeichnet.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
59
Abbildung 4.2: Arbeitsschritte des Index-Advisors
Nach der zweiten Ausführung des „Planners” werden die „virtuellen Indizes” nicht
mehr benötigt und wieder aus der Datenbank entfernt.
Die weitere Aufgabe des „Advisors” ist, den Plan-Tree nach „virtuellen Indizes”
zu durchsuchen. Enthält dieser „virtuelle Indizes”, so bringen diese einen Geschwindigkeitsvorteil für die Anfrage. Der mögliche Performance-Gewinn ist durch die Differenz der zwei Plan-Trees zu berechnen. Zum Schluss speichert der „Advisor” sein
Ergebnis in die Datenbank.
Einen detailierteren Einblick in die einzelnen Arbeitsschritte des „Advisors” geben
die folgenden Unterkapitel.
4.2
Index-Kandidaten-Liste
Während der Arbeit des Index-Advisors stellt die Index-Kandidaten-Liste die wichtigste Datenstruktur dar. Sie wird bei vielen Arbeitsschritten ausgelesen oder modifiziert. Somit enthält die Liste den aktuellen Zustand des Index-Advisors. Dies sind im
einzelnen Informationen über:
• gefundene Index-Kandidaten
Dies sind die Attribute der Anfrage. Insbesondere die Attribute, welche noch
nicht indiziert sind und dem Regelwerk entsprechen (siehe Kapitel 4.3).
• angelegte Index-Kandidaten
Dies sind die Attribute, für die ein „virtueller Index” in der Datenbank angelegt
wurde.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
60
• genutzte Index-Kandidaten
Dies sind die Attribute, welche vom „Planner” als „virtuelle Indizes” genutzt
wurden.
typedef struct IndexCandidate
{
Index
varno;
AttrNumber varattno;
Index
varlevelsup;
Oid
vartype;
bool
converted;
Oid
reloid;
char
*colname;
Oid
idxoid;
bool
idxused;
} IndexCandidate;
Abbildung 4.3: Definition des IndexCandidates
Die Index-Kandidaten-Liste wird mittels der PostgreSQL-List-package aufgebaut.
Diese wurde in Kapitel 3.5 vorgestellt. Die Listenelemente sind vom Typ „IndexCandidate”, welcher eigens dafür implementiert wurde. Sein Aufbau ist in der Abbildung
4.3 dargestellt. Dabei bedeuten die Variablen im einzelnen:
• varno
Diese Variable verweist auf den Rangetable des Query-Trees und gibt damit eine
Relation an. Der Wert der Variablen wird vom „Advisor” nur vorübergehend benötigt und in einem späteren Arbeitsschritt in eine „RelationsId” umgewandelt.
• varattno
Die Nummer des Attributes zu der Relation von „varno” wird in dieser Variable gespeichert. Auch dieser Wert ist nur ein Zwischenergebnis. Der endgültige
Wert ist der Name des Attributes, welcher anhand von „varno” und „varattno”
ermittelt werden kann.
• varlevelsup
Die Werte von „varno” und „varattno” beziehen sich auf einen Rangetable. Von
diesem kann es in einem Query-Tree mehrere geben. Zum Beipsiel kann eine
Unteranfrage einen eigenen Rangetable haben. Auf welchen Rangetable sich
das Attribut bezieht, wird mit „varlevelsup” festgelegt.
• vartype
Der Typ des Attributes ist in „vartype” abgelegt. Beispiele für Typen sind
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
61
„string”, „float point” und „integer”. Der Typ des Attributes wird vom „Advisor” beim Anlegen der „virtuellen Indizes” benötigt. Nur mit der Kenntnis des
Attribut-Types kann die zugehörige Operator-Class (siehe Kapitel 3.9.3) des Indexes ermittelt werden. Das Anlegen des Indexes ist ohne sie nicht möglich.
• converted
Nachdem die Umwandlung von den Rangetableverweisen („varno” und „varattno”) erfolgt ist, wird diese Variable auf „true” gesetzt. Sie ist damit der Indikator,
ob die Umwandlung bereits durchgeführt wurde oder nicht.
• reloid
Dies ist die „RelationsId”, welche anhand des Wertes von „varno” und dem Rangetable ermittelt wird. Nur die „RelationsId” ist in der Datenbank eindeutig und
kann vom Index-Advisor genutzt werden.
• colname
Hinter „colname” verbirgt sich der Name des Attributes. Dieser wird auch aus
dem Rangetable ausgelesen. Dazu werden die Werte der Variablen „varno” und
„varattno” benutzt. Die Kombination von „colname” und „reloid” „adressiert”
ein Attribut der Datenbank eindeutig.
Des Weiteren ist es dem Index-Advisor möglich, anhand der Variable „reloid”
zusammen mit „colname”, doppelte Attribute in der Index-Kandidaten-Liste zu
finden und danach zu entfernen.
• idxoid
Beim Erstellen eines „virtuellen Index” wird diesem vom Datenbanksystem eine
eindeutige „Id” zugeordnet. Die „Id” wird für alle Manipulationen an dem Index
benötigt. Nach dem Anlegen des Indexes speichert der „Advisor” die zugehörige
„Id” in die Variable „idxoid”.
• idxused
Diese Variable stellt das Ergebnis des Index-Advisors dar. Nur wenn der „virtuelle Index” des Attributes vom „Planner” genutzt wurde, hat „idxused” den Wert
„true”. Die Variable dient damit zur Erstellung der Advisor-Empfehlung.
Die meisten Variablen werden nicht in allen Arbeitsschritten des Index-Advisors benötigt. Viel mehr haben sie nur eine Bedeutung für einen definierten Einsatzbereich
oder werden im Laufe der Verarbeitung durch andere ersetzt.
4.3
Regelwerk
Die Arbeitszeit des Index-Advisors sollte so kurz wie möglich sein. Je mehr Zeit der
„Advisor” benötigt, desto größer wird die Reaktionszeit des Datenbanksystems. Dies
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
62
fällt besonders bei sehr „kleinen” Anfragen auf, für deren Beantwortung nicht viel Zeit
benötigt wird.
Deshalb ist das Ziel des Regelwerks die Reduzierung der Arbeitszeit des IndexAdvisors. Dies wird mit der Verminderung der Anzahl der Index-Kandidaten erreicht.
Dabei soll die Qualität der Empfehlung des „Advisors” gleich bleiben.
Eine Verringerung der Index-Kandidaten-Anzahl führt zu weniger Aufwand in allen Arbeitsschritten des „Advisors”. Solch eine Reduzierung ist möglich, da ein Index
nur bei einem kleinen Teil der Attribute der Anfrage einen Vorteil bringt. Dieser Aspekt
wurde bereits in der Einführung zu Kapitel 4 näher beschrieben. Die Reduzierung der
Index-Kandidaten bringt insbesondere einen Vorteil bei folgenden Arbeitsschritten:
• Anfrageplanung
Wie in Kapitel 4.1 beschrieben wurde, nutzt der Index-Advisor den „Planner”
zweimal. Dabei steigt die vom „Planner” benötigte Zeit mit der Anzahl möglicher Ausführungspläne an. Eine große Anzahl „virtueller Indizes” erhöht die
Anzahl dieser Pläne stark. Aus diesem Grund sollten die Anzahl der „virtuellen
Indizes” und damit die Anzahl der Index-Kandidaten gering gehalten werden.
• Anlegen von „virtuellen Indizes”
Die Zeit zur Erstellung eines „virtuellen Index” ist nur ein Bruchteil der Zeit die
für einen „realen” Index benötigt wird. Trotzdem fällt sie in der Gesamtzeit des
Index-Advisors ins Gewicht. Eine Reduzierung der „Kandidaten” führt auch zu
einer Verminderung der „virtuellen Indizes”.
Das Regelwerk des Index-Advisors besteht aus drei Regeln. Diese werden der
Reihenfolge nach abgearbeitet. Erfüllt mindestens ein Attribut eine Regel, so werden
die verbleibenden Regeln nicht mehr überprüft. Die Regeln lauten:
Ein „virtueller Index” wird für alle Attribute erstellt die...
1. ...sich in der „Where-Klausel” befinden und mit einem der Operatoren „<”, „>”,
„<=”, „>=”, „=” oder „LIKE” verknüpft sind.
2. ...sich in der „Group-by-Klausel” befinden.
3. ...sich in der „Order-by-Klausel” befinden.
Das Regelwerk ist sehr einfach gehalten. Dies ermöglicht eine schnelle und einfache Überprüfung der Regeln. Des Weiteren steigt der Aufwand mit genaueren Regeln
enorm an. Die Schwierigkeit dabei ist, dass die Regeln nicht das Ergebnis des „Advisors” verändern dürfen. Welche Indizes der „Advisor” empfiehlt, hängt direkt von der
Entscheidung des „Planners” ab (siehe Kapitel 4.1). Die Regeln müssten sich also sehr
am „Planner” orientieren und seine Entscheidungen „vorhersagen”. Dies ist sehr aufwendig und kann bereits durch eine neue PostgreSQL-Version nicht mehr stimmen.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
63
Aus diesem Grund wurden sehr einfache Regeln gewählt. Sie schränken die IndexKandidaten-Anzahl stark ein, ohne dabei sehr vom „Planner” abhängig zu sein.
Anhand eines Beispiels soll der Einsatz des Regelwerkes verdeutlicht werden. Die
dafür genutzte Anfrage ist in Abbildung 4.4 dargestellt. Zur Ermittlung der IndexKandidaten wird als erstes Regel 1 überprüft. Diese erfüllt keines der Attribute, da es in
der Anfrage keine „Where-Klausel” gibt. Es folgt die Überprüfung der zweiten Regel.
Mit dieser stimmt das Attribut „l_orderkey” überein und wird als Index-Kandidat aufgenommen. Die dritte Regel kommt nicht zur Anwendung, da bereits eine Regel erfüllt
wurde. Damit ist der einzige Index-Kandidat der Anfrage das Attribut „l_orderkey”.
SELECT l_orderkey,l_quantity
FROM lineitem
GROUP BY l_orderkey;
Abbildung 4.4: Beispiel-Anfrage2
4.4
Virtuelle Indizes
Die Entscheidung, welche Indizes für eine Anfrage von Nutzen sind, werden vom
„Planner” getroffen. Das PostgreSQL-Datenbanksystems verfügt aber nur über „reale
Indizes”. Das bedeutet: Für die Entscheidung des „Planners” müsste für jedes Attribut der Anfrage ein „realer” Index angelegt werden. Je nach der Komplexität einer
Datenbank, kann dies nur Sekunden aber auch Stunden dauern. Solch ein Aufwand
ist im Rahmen des Index-Advisors nicht vertretbar und würde die Datenbank nicht
performanter machen.
Die Lösung ist die Einführung eines neuen Index-Types, den sogenannten „virtuellen Indizes”. Ihre Erstellung dauert nur einen Bruchteil einer Sekunde, unabhängig von
der Größe der Datenbank. Der „Planner” macht bei seiner Arbeit keinen Unterschied
zwischen den Index-Typen. Somit sind „virtuelle Indizes” ein vollwertiger Ersatz für
„reale Indizes” bei der Planungsphase.
„Virtuelle Indizes” gewinnen ihren Geschwindigkeitsvorteil aus der Tatsache, dass
ihre Index-Datei nicht gefüllt ist. Sie existieren nur im System-Catalog (siehe Kapitel
3.9) von PostgreSQL und werden ausschließlich für den Index-Advisor eingesetzt. Zur
Erstellung eines „virtuellen Index” muss zwischen dem SQL-Text „CREATE INDEX”
das Schlüsselwort „VIRTUAL” eingefügt werden. Der allgemeine Syntax lautet damit
wie folgt:
CREATE VIRTUAL INDEX name ON tabelle (spalte);
Wie bereits beschrieben, wird ein „virtueller Index” vom „Planner” in seine Planung
mit einbezogen. Problematisch daran ist die Tatsache, dass der „Planner” für seine
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
64
Arbeit die Statistiken des Index auswertet. Diese sind die zwei Werte Tupelanzahl und
Disk-Pages.
Die Tupelanzahl beschreibt, wieviele Tupel der Index indiziert hat. Dieser Wert
entspricht bei den „virtuellen Indizes” der Anzahl der Tupel der zugehörigen Relation
und ist somit einfach zu ermitteln. Die Disk-Pages (siehe Kapitel 3.8) hingegen sind
nicht einfach von der zugehörigen Relation abzuleiten. Mittels der Disk-Pages wird
die Größe des Index beschrieben. Die Anzahl der Disk-Pages kann zwar berechnet
werden, allerdings bestehen dabei folgende Probleme:
1. Füllung der Disk-Pages
Die einzelnen Disk-Pages können unterschiedlich gut gefüllt werden. Dies hängt
von der Größe der gespeicherten Tupel ab. Eine zuverlässige Vorhersage der
Füllung pro Disk-Page ist nur sehr schwer möglich.
2. Größe des indizierten Attributes
Zur Berechnung wird die Größeninformation des Attributes benötigt auf welches
sich der Index bezieht. Dabei haben nicht alle Attribute eine bekannte Größe.
Diese Tatsache wird im Weiteren noch genauer beschrieben.
Von den beiden Punkten kann der erste über einen Durchschnittswert gut kompensiert
werden. Die Berechnung ist zwar nicht exakt, reicht im Rahmen des Index-Advisors
aber aus. Der zweite Punkt, die Größe des indizierten Attributes, ist abhängig von dem
Typ des Attributes. Dazu lassen sich die Typen in vier Kategorien einteilen. Diese sind
in der Abbildung 4.5 dargestellt. Diese Kategorien sind im Einzelnen:
1. Fix
Die Attribute haben immer die gleiche Größe. Diese ist vom System fest vorgegeben und nicht veränderbar. Attribut-Typen dieser Kategorie sind z.B. „integer”
und „date”.
2. Fix zur Laufzeit
Attribute dieser Kategorie haben eine festlegbare Größe. Die Wahl der Größe
ist aber für alle Tupel gleich und kann nur über spezielle Schlüsselworte wie
„ALTER” geändert werden. Ein Attribut-Typ dieser Kategorie ist „char”.
3. Variabel zur Laufzeit
Die Größe dieser Attribute ist nur durch ein Maximalwert begrenzt. Die konkrete
Größe kann von Tupel zu Tupel unterschiedlich sein und richtet sich nach den
Daten, die das Tupel speichert. Ein Attribut-Typ dieser Kategorie ist „varchar”.
4. Unbegrenzt
Diese Attribute haben keinerlei Größenangaben. Der von ihnen verwendete
Speicher hängt ausschließlich von den gespeicherten Daten ab.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
65
Abbildung 4.5: Spaltentypen und ihre Größe
In Abhängigkeit der Kategorie, zu der ein Attribut gehört, schwankt die Qualität
der Schätzung der Disk-Pages. Für die Schätzung muss zunächst die Anzahl der Tupel
pro Disk-Page ermittelt werden. Die dazu benutzen Formeln lauten wie folgt:
• Kategorie 1 und 2
Attribute aus beiden Kategorien können identisch behandelt werden. Bei beiden
ist die Größe zur Laufzeit bekannt und kann vom „Advisor” leicht ermittelt
werden. Die Berechung der Tupel pro Disk-Page erfolgt dabei nach der in
Abbildung 4.6 dargestellten Formel. Dabei wird eine Abschätzung erreicht, die
sehr gut ist und zu 90-100% einem „realen” Index entspricht.
T uplesP erP age =
P ageSize − P ageOverhead
T upleSize + T upleOverhead
Abbildung 4.6: Formel - Kategorie 1 und 2
• Kategorie 3
Die Größe der Attribute dieser Kategorie ist nur durch einen Maximalwert
begrenzt. Aus diesem Grund sind die Formeln für die Schätzung der Tupel pro
Disk-Pages etwas anders. Alle Formeln sind in Abbildung 4.7 ersichtlich. Die
Qualität der Abschätzung kann als durchschnittlich eingestuft werden. Sie hängt
sehr stark von den Daten der Tupel ab.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
66
V arT upleSize = T upleSize ∗ T upleF ill
T uplesP erP age =
P ageSize − P ageOverhead
V arT upleSize + T upleOverhead
Abbildung 4.7: Formeln - Kategorie 3
• Kategorie 4
Eine Abschätzung der Tupel pro Disk-Pages für die Attribute dieser Kategorie
ist sehr schwer. Die Abschätzung wird umso ungenauer, je mehr Attribute der
Kategorie 3 und 4 in einer Relation sind. Um trotz der unbekannten Größe
eine Abschätzung machen zu können, wird für die Berechnung die Anzahl
der Disk-Pages der indizierten Relation benutzt. Zusammen mit der Anzahl
der Attribute der Kategorie 4 ergeben sich die in Abbildung 4.8 dargestellten
Formeln.
Eine Aussage über die erreichte Qualität der Abschätzung ist nicht möglich. Sie
hängt völlig von den Daten der Tupeln ab.
RelationSize = RelationDiskP ages ∗ P ageSize
RelationT upleF ixSize = SumT upleF ixSize ∗ RelationT uples
V arT upleSize =
RelationSize − RelationT upleF ixSize
÷ RelationT uples
CountT upleV arSize
T uplesP erP age =
P ageSize − P ageOverhead
V arT upleSize + T upleOverhead
Abbildung 4.8: Formeln - Kategorie 4
Die in den Formeln verwendeten Variablen bedeuten im Einzelnen:
• PageSize
Die Größe einer „Disk-Page” in Byte. Dies ist ein vom Datenbanksystem vorgegebener Wert.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
67
• PageOverhead
Der Overhead pro „Disk-Page” in Byte. Er setzt sich aus der Größe des „Pageheaders” und des „Special Spaces” zusammen.
• TupleSize
Die Größe eines Tupels in Byte, wobei das Attrbribut zu der Kategorie 1 oder 2
gehört.
• VarTupleSize
Die Größe eines Tupels in Byte, wobei das Attrbribut zu der Kategorie 3 oder 4
gehört.
• TupleOverhead
Der Overhead pro Tupel in Byte. Dieser besteht aus dem „Tupelheader”, „Indexheader” und „Itempointer” pro Tupel.
• RelationTuples
Die Anzahl der Tupel in der indizierten Relation.
• TupleFill
Der Faktor, der angibt, wie gut das Attribut durchschnittlich gefüllt ist. Dabei
wurde 0,75 verwendet.
• RelationDiskPages
Die Anzahl der „Disk-Pages” der indizierten Relation.
• SumTupleFixSize
Die Summe der Größe aller Attribute der Kategorien 1, 2, 3 in der indizierten
Relation.
• CountTupleVarSize
Die Anzahl der Attribute der Kategorie 4 in der indizierten Relation.
• TuplesPerPage
Die errechnete Anzahl der Tupel pro Disk-Page.
• RelationSize
Die Größe einer Relation in Bytes.
• RelationTupleFixSize
Die Summe der Größe aller Attribute der Kategorie 1 und 2.
Mit der Schätzung der Tupel pro Disk-Page kann die Größe des Index berechnet werden. Die dazu genutzte Formel ist für alle Kategorien gleich und in Abbildung 4.9
dargestellt. Sie berechnet die Index-Größe in Disk-Pages.
Die verwendeten Variablen werden im Folgenden beschrieben:
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
68
DiskP ages =
RelationT uples
÷ P ageF ill
T uplesP erP age
Abbildung 4.9: Formel - Index-Größe
• PageFill
Der Faktor, mit dem durchschnittlich eine Indexseite gefüllt wird. Er beträgt
0,92.
• RelationTuples
Die Anzahl der Tupel in der indizierten Relation.
• TuplesPerPage
Die errechnete Anzahl der Tupel pro Disk-Page.
Die Qualität der Abschätzung der Index-Größe hängt stark von der Kategorie des Attributes ab. Die Kategorien 1 und 2 liefern dabei die besten Ergebnisse. Die Abschätzung
für Kategorie 3 und 4 liefern nur ungenaue Ergebnisse, sind aber in Anbetracht der
schnellen Berechnung akzeptabel.
Vorteilhaft ist, dass Attribute der Kategorie 1 und 2 am häufigsten in Datenbanken vorkommen und in Anfragen genutzt werden. Dies bedeutet, sie werden auch am
meisten indiziert und stellen die Hauptaufgabe für den Index-Advisor dar.
4.5
Systemtabelle pg_indexadvisor
Wie bereits im Kapitel 3.9 beschrieben, verfügt PostgreSQL über einen SystemCatalog. In diesem werden die Meta-Daten des Datenbanksystems abgelegt. Für den
Index-Advisor wurde der System-Catalog um eine Relation erweitert. Entsprechend
ihrer Zugehörigkeit, trägt die Relation den Namen „pg_indexadvisor”. Als Systemtabelle wird sie vom Datenbanksystem automatisch angelegt.
Aufgabe der Relation ist die Speicherung der Ergebnisse des Index-Advisors. Dies
sind die ermittelten nützlichen Index-Kandidaten. Für diesen Zweck hat die Relation
drei Attribute, welche im Folgenden beschrieben werden:
• „advisetable”
Dieses Attribut ist vom Typ „Oid”. Damit wird die Relation angegeben, in welcher sich das Attribut eines Index-Kandidaten befindet.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
69
• „adviseattr”
Das Attribut „adviseattr” ist vom Typ Integer. Mit der enthaltenen Zahl wird ein
Attribut der Relation „advisetable” referenziert.
• „adviseprofit”
Der mögliche Performancegewinn durch die Indizierung von „adviseattr” ist in
diesem Attribut abgelegt. Er wird, wie auch die Kosten einer Anfrage, in DiskPages angegeben.
In der Abbildung 4.10 ist die Tabelle „pg_indexadvisor” beispielhaft dargestellt. Insgesamt sind fünf Einträge vorhanden. Diese können aus der Analyse einer einzigen
oder aus der Analyse verschiedener Anfragen stammen. Mehrere Einträge mit derselben Kombination von „advisetable” und „adviseattr” sind üblich. Der Grund dafür ist,
dass der „Advisor” den „adviseprofit” nicht summiert. Jede Anfrage wird einzeln betrachtet. Kommt eine Anfrage sehr häufig vor, so werden ihre Index-Kandidaten oft in
der Tabelle „pg_indexadvisor” stehen.
Abbildung 4.10: Systemtabelle „pg_indexadvisor”
4.6
Ablauf
Bei jeder komplexen Anfrage wird der Index-Advisor vom Datenbanksystem aufgerufen. Er führt dann einen festen Ablauf von Arbeitsschritten durch und speichert sein
Ergebnis in die Datenbank. Die einzelnen Arbeitsschritte und ihr zeitlicher Ablauf
werden in diesem Unterkapitel beschrieben.
Eine Übersicht des Index-Advisor-Ablaufes gibt die Abbildung 4.11. Dabei ist der
Ablauf in einzelne Funktionsblöcke gegliedert. Diese Blöcke stellen jeweils einen Arbeitsschritt dar und sind zur besseren Beschreibung nummeriert.
Der Ablauf in jedem Funktionsblock ist mit Pseudo-Code dargestellt. Dies soll die
einzelnen Arbeitsschritte verständlicher machen, ohne verwirrende Details des eigentlichen Quellcodes. Die Blöcke werden im Anschluss noch genauer beschrieben.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
70
Abbildung 4.11: Ablauf des Index-Advisors
4.6.1
Input
Der oberste Block ist kein Funktionsblock und ist deshalb nur gestrichelt umrandet.
Er stellt die Definition der Index-Advisor-Funktion dar. Anhand dieser ist ersichtlich,
welche Eingabewerte der „Advisor” vom Datenbanksystem erhält. Der Input-Block ist
in Abbildung 4.12 nochmals dargestellt.
Der einzige Input des Index-Advisors ist ein Zeiger auf den Query-Tree. Dieser
repräsentiert die Anfrage und ist damit die Basis für die Arbeit des „Advisors”. Der
genaue Aufbau und die Funktion des Query-Trees wurde bereits in Kapitel 3.6.2 beschrieben.
Abbildung 4.12: Ablauf des Index-Advisors - Input
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
4.6.2
71
Ausführung ohne virtuelle Indizes
Um einen Performancegewinn ermitteln zu können, benötigt der „Advisor” einen Vergleichswert. Dieser wird im ersten Arbeitsschritt gebildet. In der Abbildung 4.13 ist
der zugehörige Funktionsblock abgebildet. Als erster Arbeitsschritt trägt er auch die
Nummer „1”.
Zur Ermittlung des Vergleichswertes ruft der Index-Advisor den Planner auf. Dieser benötigt als Input den Query-Tree der Anfrage. Während der Arbeit des Planners
liest dieser nicht nur den Query-Tree aus, sondern modifiziert ihn auch. Dies dient
der Optimierung der Anfrage, verändert aber den Query-Tree unwiederbringlich. Mit
solch einem modifizierten Query-Tree ist keine erneute Anfrageplanung möglich.
Aus diesem Grund wird von dem Query-Tree eine Kopie angefertigt. Dies erfolgt
in der ersten Zeile des Funktionsblockes. Die dazu genutzte Funktion „copyObject”
wird von PostgreSQL bereitgestellt. Sie macht von der Baumstruktur des Query-Trees
eine Tiefenkopie.
Mit der erstellten Tiefenkopie wird in der zweiten Zeile der Planner aufgerufen.
Seine Modifikationen erfolgen nun ausschließlich an der Kopie. Das Ergebnis des
Planners ist ein Plan-Tree, welcher in der Variable „plan” gespeichert wird.
In der dritten und letzten Zeile des Funktionsblockes werden die vom Planner geschätzten Kosten ausgelesen. Die Variable „orgCost” speichert sie bis zum Vergleich
mit den optimierten Kosten.
Abbildung 4.13: Ablauf des Index-Advisors - Ausführung ohne virtuelle Indizes
4.6.3
Index-Kandidaten
Für alle weiteren Arbeitsschritte benötigt der Index-Advisor die so genannten IndexKandidaten. Dies sind alle Attribute der Anfrage, deren Indizierung einen Performancegewinn bringen können. Die Ermittlung der Index-Kandidaten erfolgt im zweiten
Funktionsblock. Dieser ist in der Abbildung 4.14 dargestellt.
Zur Bestimmung der Index-Kandidaten wird das in Kapitel 4.3 vorgestellte Regelwerk benutzt. Dabei überprüfen die Regeln u.a. die Operatoren zwischen den Attributen. Solche sind z.B. „=”, „<=” oder „>”. Im Query-Tree werden die Operatoren
allerdings nicht in dieser Form gespeichert. Der Parser konvertiert die textuelle Form
der Operatoren in Oid’s. Dies sind Zahlen, welche jeden Operator eindeutig identifizieren.
Somit führt die erste Zeile des Funktionsblockes das Auslesen der Operator-Oid’s
aus. Dazu wird die dafür erstellte Funktion „getOpnos” aufgerufen. Sie liest aus dem
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
72
Abbildung 4.14: Ablauf des Index-Advisors - Index-Kandidaten
System-Catalog alle Operator-Oid’s aus, die der B-Tree (siehe Kapitel 3.1.3) unterstützt. In der Variable „opnos” werden diese für ihre Verwendung gespeichert.
Die zweite Zeile des Funktionsblockes führt die Ermittlung der Index-Kandidaten
durch. Dies geschieht in der Funktion „scanQuery”. Sie durchläuft den Query-Tree
und überprüft gleichzeitig die Regeln des Regelwerkes. Als Parameter benötigt „scanQuery” den Query-Tree und die bereits beschriebenen Operator-Oid’s. Das Ergebnis
ist die Index-Kandidaten-Liste (siehe Kapitel 4.2), welche in der Variable „candidates”
abgelegt wird.
Nicht alle gefundenen Index-Kandidaten können vom „Advisor” genutzt werden.
Die Funktion „cleanup” entfernt die nicht nutzbaren Kandidaten. Dieser Arbeitsschritt
erfolgt in der dritten Zeile. Entfernt werden dabei folgende Index-Kandidaten:
• doppelte Kandidaten
Ein Attribut kann mehrfach in einer Anfrage auftauchen. Dadurch ist es auch
möglich, dass dieses Attribut mehrmals in der Index-Kandidaten-Liste steht.
Solche Einträge werden entfernt, damit jedes Attribut maximal einmal in der
Liste vorkommt.
• bereits indizierte Kandidaten
Im Query-Tree ist nicht erkennbar, ob ein Attribut bereits indiziert ist. Dies muss
für jeden Index-Kandidaten überprüft werden. Ist bereits ein Index vorhanden,
wird der Kandidat aus der Liste entfernt, denn eine doppelte Indizierung macht
keinen Sinn.
Für die Überprüfung werden alle Attribute der Datenbank ermittelt, die bereits
indiziert sind. Dabei werden allerdings nur die Relationen betrachtet die auch in
der Kandidaten-Liste vorkommen. Beide Listen, also die der bereits indizierten
Attribute und die der Index-Kandidaten werden verglichen. Gibt es Übereinstimmungen, so werden die entsprechenden Kandidaten entfernt.
• Kandidaten von Systemrelationen
Das Indizieren von Attributen aus dem System-Catalog kann nicht zur Laufzeit erfolgen. Sind solche Attribute unter den Index-Kandidaten, müssen sie entfernt werden. Diese Überprüfung erfolgt mittels der PostgreSQL-Funktion „IsSystemRelation”.
• Kandidaten von temporären Tabellen
Während der Anfrageverarbeitung können temporäre Tabellen erstellt werden.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
73
Diese enthalten Zwischenergebnisse und werden nach Abschluss der Verarbeitung wieder gelöscht. Bei temporären Tabellen gilt ähnliches wie bei den Systemrelationen. Auch sie dürfen nicht indiziert werden.
Ein Teil einer Anfrage kann als temporäre Tabelle vorliegen. Dies können z.B.
die Tupel einer Unteranfrage sein. Dadurch ist es möglich, dass die IndexKandidaten-Liste Attribute von temporären Tabellen enthält. Auch diese Kandidaten müssen entfernt werden.
Die Überprüfung erfolgt anhand der Parameter einer Tabelle. Ist der Wert des Parameters „rd_istemp” gleich true, so handelt es sich um eine temporäre Tabelle
und der zugehörige Kandidat wird entfernt.
Nach dem Aufruf von „cleanup” sind nur noch „gültige” Einträge in der IndexKandidaten-Liste. Diese können in den weiteren Arbeitsschritten des Index-Advisors
auf ihren Nutzen überprüft werden.
4.6.4
Anlegen der virtuellen Indizes
Der dritte Funktionsblock ist für das Anlegen der „virtuellen Indizes” zuständig. Dabei wird für jeden Index-Kandidaten ein „virtueller Index” angelegt. Dies geschieht in
der ersten Zeile des Funktionsblockes und wird durch die Funktion „createVirtualIndizes” symbolisiert. Die Bedeutung und Funktionsweise der „virtuellen Indizes” wurde
bereits im Kapitel 4.4 beschrieben.
In der zweiten Zeile erfolgt ein Aufruf der Funktion „CommandCounterIncrement”. Mit dieser werden die erstellten Indizes „sichtbar” gemacht. Ohne den Aufruf würde dies erst mit dem Abschluss der Anfrageverarbeitung erfolgen. Der Grund
ist, dass jede Anfrage in einer eigenen Transaktion verarbeitet wird. Ohne den Aufruf
von „CommandCounterIncrement” würden die „virtuellen Indizes” in dem folgenden
Arbeitsschritt vom Planner nicht genutzt werden. Eine ausführliche Beschreibung des
„CommandCounters” ist in Kapitel 3.7 zu finden.
Abbildung 4.15: Ablauf des Index-Advisors - Anlegen der virtuellen Indizes
4.6.5
Ausführung mit virtuellen Indizes
Nachdem die „virtuellen Indizes” angelegt wurden, wird in diesem Arbeitsschritt der
mögliche Performancegewinn ermittelt. Dazu wird erneut der Planner genutzt. Alle
dafür notwendigen Aufrufe sind in der Abbildung 4.16 dargestellt.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
74
In der ersten Zeile erfolgt ein Aufruf der Funktion „copyObject”. Ihre Aufgabe
wurde bereits im ersten Funktionsblock beschrieben. Eine weitere Kopie des QueryTrees ist notwendig, da der Planner nochmals aufgerufen wird. Zwar wird der QueryTree danach vom „Advisor” nicht mehr benötigt, allerdings hat der Index-Advisor nur
einen Zeiger auf den Query-Tree bekommen. Eine Modifikation an diesem „Original”
bedeutet, der Query-Tree wird unwiederbringlich zerstört. Eine Weiterverarbeitung der
Anfrage in der PostgreSQL-Pipeline wäre nicht mehr möglich.
Wie schon im ersten Funktionsblock, wird der Planner mit der Kopie des QueryTrees aufgerufen. Dies geschieht in der zweiten Zeile. Der Planner erstellt nun für die
Anfrage einen Ausführungsplan. Im Gegensatz zu seinem ersten Aufruf stehen ihm
mehr Möglichkeiten zur Verfügung. Er kann die „virtuellen Indizes” in seinem PlanTree nutzen. In der Variable „plan” wird dieses Ergebnis gespeichert.
Die dritte Zeile des Funktionsblockes dient der Ermittlung des Performancegewinns. Dazu werden mit „getCost” die Kosten des gerade erstellten „Plan-Trees” ausgelesen. Die Differenz dieser Kosten zu den Kosten des ersten Planner-Aufrufes ergeben den Gewinn. Dieser wird in der Variable „savedCost” gespeichert.
Der letzte Schritt des Funktiosblockes ist die Ermittlung der „virtuellen Indizes”,
welche vom Planner genutzt wurden. Dies erfolgt in der Funktion „scanPlan”. Wie
bereits aus dem Namen ableitbar ist, durchläuft sie den erstellten Plan-Tree. Dies ist
auch ihr erster Parameter. In dem Plan-Tree können mehrere Indizes vorkommen. Dabei ist keine Unterscheidungsmöglichkeit zwischen „realen” und „virtuellen Indizes”
enthalten. Dies ist der Grund für den zweiten Parameter der Funktion, welche die
Index-Kandidaten-Liste ist. Ein Vergleich der Kandidaten-Liste mit allen Indizes im
Plan-Tree ermöglicht das Finden der „virtuellen Indizes”, sofern vom Planner einige
genutzt wurden. Die Information, welcher der „virtuellen Indizes” genutzt wurde, wird
in der Variable „used” abgelegt.
Abbildung 4.16: Ablauf des Index-Advisors - Ausführung mit virtuellen Indizes
4.6.6
Entfernen der virtuellen Indizes
Durch alle bisher beschriebenen Arbeitsschritte liegt das Ergebnis des „Advisors” bereits vor. Dies sind die genutzten „virtuellen Indizes” und die gesparten Kosten. Die
Aufgabe des fünften und damit vorletzten Funktionsblockes ist das Entfernen der „virtuellen Indizes”. Der Funktionsblock ist in Abbildung 4.17 dargestellt.
Die Funktion „dropVirtualIndizes” in der ersten Zeile entfernt alle angelegten „virtuellen Indizes”. Ihr einziger Parameter ist die Index-Kandidaten-Liste. Diese durch2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
75
läuft die Funktion und entfernt den „virtuellen Index” zu jedem Kandidaten.
In der zweiten Zeile schließt sich der Aufruf der Funktion „CommandCounterIncrement” an. Dieser Aufruf soll die Entfernung der Indizes „sichtbar” machen. Ohne
der Erhöhung des „CommandCounters” ständen die „virtuellen Indizes” noch bis zum
Abschluss der Anfrageverarbeitung zur Verfügung. Erscheinen sie im Plan-Tree, so
werden sie auch vom Executor genutzt. Aber für solch einen Einsatz sind die „virtuellen Indizes” nicht vorgesehen.
Abbildung 4.17: Ablauf des Index-Advisors - Entfernen der virtuellen Indizes
4.6.7
Speicherung des Ergebnisses
Der letzte Arbeitsschritt des Index-Advisors ist die Speicherung des Ergebnisses. Der
dazugehörige Funktionsblock ist in der Abbildung 4.18 dargestellt. Dabei führt die
Funktion „insert” die Speicherung durch. Sie ist in der ersten Zeile zu finden.
Der erste Parameter der Funktion ist der Name der Tabelle, in welche das Ergebnis
gespeichert werden soll. Für den Index-Advisor lautet dieser „pg_indexadvisor”. Diese
Tabelle ist ein Teil des System-Catalogs und wurde in Kapitel 4.5 vorgestellt.
In die Tabelle „pg_indexadvisor” fügt die Funktion einen Eintrag für jeden IndexKandidaten ein, dessen „virtueller Index” vom Planner genutzt wurde. Die entsprechenden Kandidaten werden mittels der Variable „used” übergeben.
Mit dem dritten Parameter werden die gesparten Kosten und damit der Gewinn
der Funktion mitgeteilt. Der Gewinn wird durch die Anzahl der genutzten IndexKandidaten geteilt und zu jedem Kandidaten abgespeichert.
Die entstandenen Einträge in der Tabelle „pg_indexadvisor” können von weiteren
Routinen oder Applikation ausgewertet werden, wie z.B. vom QUIET-System (siehe
Kapitel 2.4). Dabei müssen Statistiken über bestehende Indizes von diesen Systemen
gebildet werden. Der Index-Advisor betrachtet nur nicht vorhandene Indizes.
In der zweiten Zeile des Funktionsblockes wird der Index-Advisor beendet.
Abbildung 4.18: Ablauf des Index-Advisors - Ergebnis
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
4.7
76
Implementierung
Bei der Implementierung des „Index-Advisors” wurden verschiedene Dateien modifiziert und hinzugefügt. Die dabei erfolgten Änderungen werden in diesem Kapitel
beschrieben. Dadurch soll ein Überblick der Implementierung und dessen Verteilung
über die Quell-Dateien gegeben werden. Die erfolgten Änderungen sind im Einzelnen:
• virtuelle Indizes
Bei der Verarbeitung von Indizes werden im Wesentlichen zwei Datenstrukturen eingesetzt. Dies ist erstens der „IndexInfo-Node” (siehe Datei „include/nodes/execnodes.h”) und zweitens der „IndexStmt-Node” (siehe Datei „include/nodes/parsenodes.h”). Beide Datenstrukturen wurden um eine Variable
mit dem Namen „virtual” erweitert. Diese ist vom Typ „boolean” und gibt an,
ob der Index als virtuell angelegt werden soll.
In der Datei „backend/parser/gram.y” sind alle Regeln enthalten, nach denen eine Anfrage geparst wird. Diese Datei wurde um das Schlüsselwort
„VIRTUAL” und dessen Auswertung erweitert. Zusätzlich musste in „backend/parser/keywords.c” das Schlüsselwort „VIRTUAL” eingetragen werden.
Erst dadurch wird es vom System als solches erkannt.
Mit den modifizierten Datenstrukturen („IndexInfo-Node”, „IndexStmt-Node”)
arbeiten Funktionen. Sie müssen ebenfalls angepasst werden. Die erste Funktion trägt den Namen „DefineIndex”. Sie ist in der Datei „backend/commands/indexcmds.c” abgelegt und wird bei jeder Eingabe von
„CREATE INDEX” aufgerufen. Die Funktion „DefineIndex” wurde so angepasst, dass auch sie mit der Variable „virtual” korrekt umgeht.
Die eigentliche Erstellung des Index erfolgt durch die Funktion „index_create”.
Diese ist in der Datei „backend/catalog/index.c” zu finden. Auch diese Funktion wurde für den Umgang mit „virtual” angepasst. Des Weiteren befindet sich
in der Datei „index.c” die Implementierung der virtuellen Indizes. Dafür wurden
zwei Hilfsfunktionen mit den Namen „get_attrType” und „estimate_indexpages”
erstellt. Sie dienen dazu, die Kategorie eines Attributes zu bestimmen (siehe
Kapitel 4.4) und die Diskpages des virtuellen Index abzuschätzen. Innerhalb
der Funktion „index_create” wird die Variable „virtual” ausgewertet. Ist sie auf
„true” gesetzt, so wird der Index nicht gefüllt und seine Größe geschätzt. Dadurch entsteht ein virtueller Index.
Die Datei „backend/bootstrap/bootparse.y” und „backend/tcop/utility.c” wurden
ebenfalls für die virtuellen Indizes modifiziert. In ihnen erfolgen Aufrufe der
Funktion „DefineIndex”. Diese mussten um den Parameter „virtual” erweitert
werden.
• Index-Advisor
Der Index-Advisor ist in der Datei „backend/indexadvisor/indexadvisor.c” abgelegt. Dort sind alle Funktionen zu finden, welche in Kapitel 4.6 beschrieben
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
77
wurden. Mittels der Funktion „indexadvisor” wird er aufgerufen. Dieser Aufruf
findet in der Datei „backend/tcop/postgres.c” statt. Diese enthält die Schritte der
Verarbeitungs-Pipeline und wurde um den Aufruf des Index-Advisors erweitert.
• pg_indexadvisor
Die Definition der Relation „pg_indexadvisor” (siehe Kapitel 4.5) erfolgt in der
Datei „include/catalog/pg_indexavisor.h”. Diese wurde eigens dafür angelegt.
Die Datei „include/catalog/catname.h” wurde um die Definition des Namens der
Systemtabelle erweitert. Für das Einbinden und Anlegen der Systemtabelle ist
lediglich ein Eintrag im „Make-File” notwendig. Die restlichen Abläufe erfolgen
automatisch.
• Konfiguration
Für die „Run-time Configuration” des Index-Advisors (siehe Kapitel 4.8) wurden die Dateien „backend/utils/misc/guc.c” und „include/utils/guc_tables.h”
modifiziert. Die erste Datei wurde um die Parameter erweitert, welche den
Index-Advisor steuern. Die zweite Datei definiert die Systemtabelle, in welcher die Konfigurationswerte abgelegt werden. Auch diese wurde für den IndexAdvisor erweitert.
4.8
Advisor-Configuration
Nicht in jedem Fall kann es wünschenswert sein, dass der Index-Advisor aktiv ist.
Jedoch als fester Teil der Verarbeitungspipeline wird er immer in die Anfrageverarbeitung einbezogen.
Wie bereits im Kapitel 3.10 vorgestellt wurde, verfügt PostgreSQL über eine „Runtime Configuration”. Diese erlaubt eine vielfältige Konfiguration des Datenbanksystems. Auch die Steuerung des Index-Advisors gehört dazu. Dieser ist über folgende
zwei Parameter konfigurierbar:
• „enable_indexadvisor”
Dieser Parameter aktiviert den Index-Advisor. Mit dem Parameter kann entschieden werden, ob der „Advisor” in die Anfrageverarbeitung einbezogen wird.
Hat der Parameter den Wert „true”, so werden für jede Anfrage nützliche Indizes ermittelt und das Ergebnis in der Datenbank abgespeichert. Mit dem Wert
„false” wird der „Advisor” vom Datenbanksystem nicht aufgerufen.
Der Parameter kann mit allen Möglichkeiten der „Run-time Configuration” gesetzt werden. So auch mittels des Kommandos „SET”. Dies ermöglicht es z.B.
den „Advisor” vor einer Anfrage zu aktivieren und sofort danach zu deaktivieren. So kann der Index-Advisor nur für einen Teil von Anfragen genutzt werden.
• „enable_indexadvisor_terminaloutput”
Wenn der „Advisor” aktiviert ist, kann mittels dieses Parameters festgelegt werden, ob er sein Ergebnis grafisch darstellt. Diese Ausgabe beinhaltet eine Liste
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
78
aller Attribute, deren Indizierung von Vorteil ist, des Weiteren die Kosten der Anfrage ohne und mit diesen Indizes. Zur schnelleren Einschätzung des Ergebnisses berechnet der „Advisor” die Differenz zwischen diesen Kosten als Zahlenund prozentualen Wert. Die letzte ausgegebene Information ist die Zeit, welche
der „Advisor” für alle seine Arbeitsschritte benötigt hat. Auch der Parameter
„enable_indexadvisor_terminaloutput” kann mit allen Möglichkeiten der „Runtime Configuration” geändert werden.
INFO:
INFO:
INFO:
INFO:
INFO:
INFO:
index on „ps_partkey” used
index on „s_suppkey” used
old cost 114507..114507
new cost 8061..8061
cost saved 106446..106446, these are 92..92%
advisor: 24ms
Abbildung 4.19: Beispiel der Index-Advisor-Ausgabe
Eine Beispiel-Ausgabe des „Advisors” ist in der Abbildung 4.19 dargestellt. In
dem Beispiel schlägt der „Advisor” zwei Attribute zur Indizierung vor. Diese
lauten „ps_partkey” und „s_suppkey”. Die Kosten der Anfrage ohne diese Indizes werden mit „old cost” bezeichnet. Sie betragen minimal 114507 und auch
maximal 114507. Mit den Indizes reduzieren sich die Kosten auf genau 8061,
was mit „new cost” bezeichnet wird. Die nächste Zeile stellt den Unterschied
zwischen den Kosten dar. Dies ist der mögliche Gewinn durch die Indizierung
der zwei Attribute. Hinter „cost saved” steht als erstes der Zahlenwert, um den
sich die Kosten der Anfrage reduzieren. Die Werte sind 106446 bis 106446. Dem
schließt sich die prozentuale Rechnung an. Die Kosten lassen sich also um 92
Prozent verringern. Hinter „advisor” steht die Zeit, welche der Index-Advisor
benötigt hat. Dies sind 24ms.
Mit den zwei Parametern der „Run-time Configuration” kann das Verhalten des IndexAdvisors an die jeweiligen Bedürfnisse angepasst werden. Durch die Möglichkeit die
Parameter zur Laufzeit zu ändern, sind mit ihnen vielfältige Optimierungsstrategien
möglich.
4.9
Auswertung
Zur Einschätzung des Index-Advisors wurden praktische Test durchgeführt. Dabei sind
insbesondere die Funktionstüchtigkeit, Qualität des Ergebnisses und Geschwindigkeit
des „Advisors” interessant.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
79
Die dazu verwendete Testumgebung ist ein Computer mit AMD Athlon XP 1600+
Prozessor und 512 MB DDR-RAM. Als Betriebssystem kam Suse Linux 9 zum Einsatz. Um aussagekräftige Ergebnisse zu erhalten wurde für die Tests die Datenbank
des „TPC-H Benchmarks” eingesetzt. Eine Beschreibung dieser ist im Kapitel 3.3 zufinden. Die Datenbank wurde mit einem „Scalefactor” von 1 erzeugt und ist damit 1
GB groß.
Bei den Tests wurden die folgenden Auswertungen durchgeführt:
• Vollständigkeit der Indexempfehlung
Diese Auswertung soll darüber Aufschluss geben, ob der Index-Advisor alle Index-Kandidaten erkennt. Zum Vergleich dient eine vollständig indizierte
„TPC-H Datenbank”. Die Indizes, welche der Planner bei dieser Datenbank
nutzt, müssen vom „Advisor” in der normalen „TPC-H Datenbank” empfohlen werden.
In den durchgeführten Testfällen hat der „Advisor” alle Index-Kandidaten korrekt ermittelt. Damit lässt sich die Aussage treffen, dass das Regelwerk funktioniert und nur unnütze Attribute entfernt.
Zur Durchführung der Tests wurde für jedes Attribut einer Anfrage ein „realer
Index” erstellt. Eine Ausführung des „Explain-Kommandos” lieferte alle Indizes, welche für die Anfrage von Nutzen sind. Dies sind die Index-Kandidaten
und damit der Vergleichswert für den Index-Advisor. Nach dem Entfernen der
„realen Indizes” wurde der „Advisor” aufgerufen und sein Ergebnis ermittelt.
Ein Vergleich der beiden Ergebnisse ergab immer eine vollständige Übereinstimmung.
• Qualität der Indexempfehlung
Neben dem Finden aller Index-Kandidaten ist auch die Qualität der Empfehlung von Interesse. Damit ist die Qualität der Kostenschätzung gemeint. Diese
fließt in den errechneten Gewinn der Index-Kandidaten ein. Stimmen die Kosten
nicht, so können bezüglich des Nutzens jedes Index-Kandidaten keine korrekten
Entscheidungen getroffen werden. Programme, welche den errechneten Gewinn
auswerten, würden falsche Entscheidungen treffen.
Um die Qualität zu testen, werden die berechneten Kosten verglichen. Dabei
werden für jede Anfrage zwei Berechnungen durchgeführt. Bei der ersten existieren alle Index-Kandidaten nur als „virtuelle Indizes”. Dies entspricht dem
Ergebnis des „Advisors”. Bei der zweiten Berechnung existieren die IndexKandidaten als „reale Indizes”. Die beiden Werte werden anschließend verglichen.
Das Ergebnis ist in Abbildung 4.20 dargestellt. Die Schätzung des IndexAdvisors stimmt fast immer mit den realen Kosten überein. Damit kann die
Qualität der Indexempfehlung als gut eingestuft werden.
• Index-Advisor Overhead
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
80
Abbildung 4.20: Qualität der Kostenschätzung
Der Index-Advisor ist ein Teil der PostgreSQL-Verarbeitungspipeline. Damit beeinflusst er den Ressourcenverbrauch des Systems. Der Mehrverbrauch, welcher
durch den „Advisor” entsteht, wird als „Overhead” bezeichnet.
Die vom Index-Advisor genutzen Ressourcen sind vor allem Rechenzeit und Arbeitsspeicher. Da der Speicherverbrauch des „Advisors” minimal ist, wird dieser
nicht weiter betrachtet. Die Rechenzeit hingegen ist eine kritische Ressource bei
Datenbanksystemen. Von ihr hängt direkt die Zeit ab, welche zur Verarbeitung
einer Anfrage benötigt wird.
Zur Ermittlung des Overheads wurde wieder die Datenbank des „TPC-H Benchmarks” genutzt. Ausgewertet wurde dabei die Zeit, welche der „Advisor” für eine Anfrage benötigt. Die Zeitmessung übernimmt dabei der „Advisor” selbst, da
sie ein Teil seiner Terminal-Ausgabe ist (siehe Kapitel 3.10). Um aussagekräftigere Werte zu erhalten, wurden die Anfragen mehrmals hintereinander ausgeführt und ein Mittelwert gebildet. Dadurch können große Schwankungen durch
z.B. Diskzugriffe gemindert werden.
Das Ergebnis der Tests ist in Abbildung 4.21 dargestellt. Die vom „Advisor” benötigte Zeit reicht von nur 3ms bis zu 64ms und ist damit recht gering.
Eine bessere Einschätzung dieser Zeiten ist mit dem Vergleich zu der gesamten
Zeit für die Anfrageverarbeitung möglich. Dies wurde für die zweite Anfrage
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
81
Abbildung 4.21: Overhead des Index-Advisors
aus dem „TPC-H Benchmark” gemacht. Der dazugehörige Vergleich ist in Abbildung 4.22 dargestellt. Dabei wurde die Zeit zur Verarbeitung der Anfrage
zweimal gemessen. Die erste Ausführung erfolgte ohne jegliche Indizes. Dafür
benötigte das Datenbanksystem 75.000ms. Die 20ms des Index-Advisors sind
dagegen verschwindend gering und fallen nicht ins Gewicht. Bei der zweiten
Ausführung standen für die Anfrage alle nützlichen Indizes zur Verfügung. Damit reduzierte sich die Verarbeitungszeit auf nur 1.300ms. Aber selbst bei dieser
recht kurzen Zeit ist der Overhead des „Advisors” nicht von Interesse.
Damit kann der Overhead des Index-Advisors als minimal bezeichnet werden
und geht nur sehr gering in die Zeit zur Anfrageverarbeitung ein.
Abbildung 4.22: Verhältnis des Overheads
Die Auswertung hat gezeigt, dass die Leistungsfähigkeit des „Advisors” durchaus gut
ist. Er liefert brauchbare Ergebnisse, ohne das Datenbanksystem zu sehr zu belasten.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
4.10
82
Einsatz
Die Einsatzmöglichkeiten des Index-Advisors sind vielfältig. In jedem Fall dient er der
Optimierung einer Datenbank und schafft durch angepasste Indizes mehr Performance.
Denkbare Einsatzgebiete sind im Einzelnen:
• QUIET-System
Das QUIET-System wurde in Kapitel 2.4 vorgestellt. Für seine Funktion nutzt es
den „DB2 Advisor”. Die Gründe, welche gegen diesen sprechen, wurden ebenfalls schon beschrieben. Der Index-Advisor stellt einen vollwertigen Ersatz für
den „DB2 Advisor” dar. So könnte das QUIET-System auf das PostgreSQLDatenbanksystem portiert werden.
• Workload-Optimierung
Mittels des Index-Advisors ist es möglich, für eine Menge von Anfragen
ein Index-Set zu bestimmen. Für diese Aufgabe werden alle Anfragen des
Workloads an die Datenbank gestellt. Anhand der Systemtabelle des „Advisors”
kann der Nutzen möglicher Indizes ermittelt werden.
• Anfrage-Optimierung
Auch für einzelne Anfragen ist der Index-Advisor von Nutzen. So kann der „Advisor” vom Datenbank-Administrator eingesetzt werden, um mögliche Indizes
für eine Anfrage zu ermitteln. Das Ergebnis ist als Terminal-Output des IndexAdvisors sofort ablesbar.
• Statistik
Der „Advisor” kann auch als Index-Statistik eingesetzt werden. Durch seine Aktivierung wird er bei jeder Anfrage aufgerufen und speichert sein Ergebnis in die
Tabelle „pg_indexadvisor”. Diese kann in regelmäßigen Abständen ausgewertet
werden. Dies kann nach den verschiedensten Gesichtspunkten erfolgen. Einer
ist z.B. die Anzahl der Einträge. Ist die Tabelle leer, deutet dies daraufhin, dass
sich das Anlegen neuer Indizes nicht lohnt. Ein weiterer Anhaltspunkt ist die
Größe des Gewinns pro Eintrag. Nur bei großen Werten können neue Indizes
mehr Performance bringen.
Anhand der Auswertung können Entscheidungen zum Anlegen von Indizes getroffen werden.
4.11
Fazit
Die Umsetzung des Index-Advisors ist voll funktionsfähig und liefert gute Resultate.
Die erstellten Empfehlungen sind sehr nah an realen Indizes. Dadurch ist die Ermittlung eines optimalen Index-Sets möglich.
2004-09-30/101/IN99/2254
KAPITEL 4. INDEX-ADVISOR
83
Der dabei erzeugte Overhead des Advisors ist gering. Im Verhältnis zur kompletten
Verarbeitungszeit einer Anfrage kann er sogar vernachlässigt werden. Dies macht eine
uneingeschränkte Nutzbarkeit möglich.
Die Schätzung der Kostenersparnis ist ebenfalls gut. Auch sie stimmt meist mit
realen Indizes überein. So bringen erstellte Indizes auch den vom Advisor berechneten
Geschwindigkeitsgewinn.
Dies alles ermöglicht den praktischen Einsatz des Index-Advisors. Dabei kann er
für vielfältige Aufgaben eingesetzt werden.
2004-09-30/101/IN99/2254
KAPITEL 5. AUSBLICK
84
Kapitel 5
Ausblick
In einigen Datenbanksystemen sind bereits Ansätze zur automatischen IndexBestimmung vorhanden. Diese aber berücksichtigen jeweils nur eine Anfrage, arbeiten
nicht parallel zum Datenbanksystem oder benötigen zu viel Zeit. Damit sind sie für
Anfragegetriebenes Index-Tuning unzureichend.
Der in PostgreSQL implementierte Index-Advisor macht es möglich, alle diese
Probleme zu lösen. Durch die vollständige Integration bietet er eine schnelle und effiziente Verarbeitung. Diese erfolgt bei jeder Anfrage, welche an das Datenbanksystem
gestellt wird. Auf diese Weise kann kontinuierlich eine Statistik gebildet werden. Diese
Informationen bilden die Grundlage für Anfragegetriebenes Index-Tuning.
Für die Auswertung der Statistik wird eine eigene Applikation benötigt. Diese kann
z.B. das in Kapitel 2.4 vorgestellte QUIET-System sein. Die Aufgabe der Applikation
besteht in der Verwaltung des Index-Pools. Aus Performancegründen ist eine Integrierung dieser Software in PostgreSQL empfehlenswert.
Ein weiterer Entwicklungspunkt ist der Index-Advisor selbst. In seiner jetzigen
Umsetzung ist er nicht mehrinstanzfähig. Dies bedeutet, dass Fehler auftreten können,
sobald mehrere Instanzen des Advisors gleichzeitig arbeiten. Dieser Fall trifft ein, sobald das Datenbanksystem mehrere Anfragen parallel verarbeitet. Das Problem dabei
sind die virtuellen Indizes. Sie sind während der Optimierung einer Anfrage in der
ganzen Datenbank „sichtbar”. Werden sie in einer anderen Anfrage genutzt, entsteht
ein Fehler im Datenbanksystem.
Zu den Aufgaben des Index-Advisors gehört nicht die Einschätzung von bereits
angelegten Indizes. Für ein komplettes Anfragegetriebenes Index-Tuning sind diese
Informationen aber notwendig. Als Lösung können sie von der Applikation ermittelt
werden, welche den Advisor nutzt. Denkbar ist aber auch eine Weiterentwicklung des
Index-Advisors selbst.
Abschließend lässt sich sagen, dass der Index-Advisor praktisch einsetzbar und
funktionsfähig ist. Allerdings ohne Mehrinstanzfähigkeit und eine Applikation, die
seine Empfehlungen auswertet, kann sein Potential nicht voll ausgeschöpft werden.
2004-09-30/101/IN99/2254
ABBILDUNGSVERZEICHNIS
85
Abbildungsverzeichnis
1.1
Beispiele der Größenverhältnisse zwischen Tabelle und Index . . . . .
2.1
2.2
2.3
2.4
2.5
2.6
Oracle - „Virtual Index Wizard” ([4]) . . . . . . . . . . . . .
Architektur des „Index Selection Tool” ([3] / S.2) . . . . . . .
Architektur des „DB2 Advisors” ([1] / S.2) . . . . . . . . . .
Architektur des QUIET-Systems ([2] / S. 3) . . . . . . . . . .
„Index Configuration Monitor” des QUIET-Systems ([2] / S. 4)
Übersicht der vorgestellten Systeme . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
. .
.
.
.
.
.
.
.
.
.
.
.
.
11
12
13
15
16
17
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
3.9
3.10
3.11
3.12
3.13
3.14
3.15
3.16
3.17
3.18
3.19
3.20
3.21
3.22
3.23
3.24
Beispiel einer Index-Nutzung . . . . . . . . . . . .
Schema der TPC-H Datenbank ([16] / S.12) . . . .
Verlauf der Anfrageverarbeitung [7] . . . . . . . .
List-Package Beispiel . . . . . . . . . . . . . . . .
Definition des List-Nodes . . . . . . . . . . . . . .
Definition des Expr-Nodes . . . . . . . . . . . . .
Definition des Plan-Nodes . . . . . . . . . . . . .
Zusammenhang zwischen State-Tree und Plan-Tree
Definition des SortBy-Nodes . . . . . . . . . . . .
Beispiel-Anfrage . . . . . . . . . . . . . . . . . .
Teilmenge des Ergebnisses der Beispiel-Anfrage .
Query-Tree der Beispiel-Anfrage . . . . . . . . . .
Query-Tree Ausschnitt - Query-Node . . . . . . .
Query-Tree Ausschnitt - Rangetable . . . . . . . .
Query-Tree Ausschnitt - Jointree . . . . . . . . . .
Query-Tree Ausschnitt - Targetlist . . . . . . . . .
Query-Tree Ausschnitt - GroupClause . . . . . . .
Query-Tree Ausschnitt - HavingQual . . . . . . . .
Plan-Tree der Beispiel-Anfrage . . . . . . . . . . .
Plan-Tree Ausschnitt - Agg-Node . . . . . . . . .
Plan-Tree Ausschnitt - MergeJoin-Node . . . . . .
Plan-Tree Ausschnitt - Sort-Node . . . . . . . . . .
Plan-Tree Ausschnitt - SeqScan-Node . . . . . . .
CommandCounter-Beispiel . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19
24
26
28
29
30
31
32
33
34
34
36
37
37
38
39
40
40
42
43
44
45
46
47
2004-09-30/101/IN99/2254
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6
ABBILDUNGSVERZEICHNIS
86
3.25
3.26
3.27
3.28
3.29
3.30
3.31
Struktur einer Disk-Page . . . . . . .
Prinzip der PostgreSQL-Operatoren .
Ausschnitt der Datei „postgresql.conf”
Parameter als „Command line option”
Parameter mit Benutzer verbinden . .
Parameter mit „SET” setzen . . . . .
Parameter mit „SHOW” anzeigen . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
49
51
54
54
55
56
56
4.1
4.2
4.3
4.4
4.5
4.6
4.7
4.8
4.9
4.10
4.11
4.12
4.13
4.14
4.15
4.16
4.17
4.18
4.19
4.20
4.21
4.22
genutzte Tabellen bei der Anfrageverarbeitung . . . . . . . . .
Arbeitsschritte des Index-Advisors . . . . . . . . . . . . . . .
Definition des IndexCandidates . . . . . . . . . . . . . . . . .
Beispiel-Anfrage2 . . . . . . . . . . . . . . . . . . . . . . . .
Spaltentypen und ihre Größe . . . . . . . . . . . . . . . . . .
Formel - Kategorie 1 und 2 . . . . . . . . . . . . . . . . . . .
Formeln - Kategorie 3 . . . . . . . . . . . . . . . . . . . . . .
Formeln - Kategorie 4 . . . . . . . . . . . . . . . . . . . . . .
Formel - Index-Größe . . . . . . . . . . . . . . . . . . . . . .
Systemtabelle „pg_indexadvisor” . . . . . . . . . . . . . . . .
Ablauf des Index-Advisors . . . . . . . . . . . . . . . . . . .
Ablauf des Index-Advisors - Input . . . . . . . . . . . . . . .
Ablauf des Index-Advisors - Ausführung ohne virtuelle Indizes
Ablauf des Index-Advisors - Index-Kandidaten . . . . . . . .
Ablauf des Index-Advisors - Anlegen der virtuellen Indizes . .
Ablauf des Index-Advisors - Ausführung mit virtuellen Indizes
Ablauf des Index-Advisors - Entfernen der virtuellen Indizes .
Ablauf des Index-Advisors - Ergebnis . . . . . . . . . . . . .
Beispiel der Index-Advisor-Ausgabe . . . . . . . . . . . . . .
Qualität der Kostenschätzung . . . . . . . . . . . . . . . . . .
Overhead des Index-Advisors . . . . . . . . . . . . . . . . . .
Verhältnis des Overheads . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
58
59
60
63
65
65
66
66
68
69
70
70
71
72
73
74
75
75
78
80
81
81
2004-09-30/101/IN99/2254
. . .
. . .
. .
. . .
. . .
. . .
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
LITERATURVERZEICHNIS
87
Literaturverzeichnis
[1] G. Valentin, M. Zuliani, D. Zilio, G. Lohman, A. Skelley: „DB2 Advisor: An
Optimizer Smart Enough to Recommend Its Own Indexes”, In Proceedings of
ICDE 200, S. 1-10, 2000
[2] K. U. Sattler, I. Geist, E. Schallehn: „QUIET: Continuous Query-driven Index
Tuning”, Proceedings of the 29th VLDB Conference, S. 1-4, Berlin, Germany,
2003
[3] S. Chaudhuri, V. Narasayya: „An Efficent, Cost-Driven Index Selection Tool for
Microsoft SQL Server”, Proceedings of the 23rd VLDB Conference, S. 1-10,
Athens, Greece, 1997
[4] Oracle: „Data sheet: Oracle Tuning Pack”, www.oracle.com
[5] Oracle: „Test Drive An Index With the Virtual Index Wizard”, www.oracle.com
[6] S. Simkovics: „Enhancement of the ANSI SQL Implementation of PostgreSQL”,
www.urv.net, S. 52-60
[7] B. Momjian: „How PostgreSQL Processes a Query”, developer.postgresql.org
[8] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. i-ii
[9] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 164-171
[10] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 913-914
[11] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 916
[12] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 917-918
[13] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 482, 936
2004-09-30/101/IN99/2254
LITERATURVERZEICHNIS
88
[14] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 487-493, 935
[15] The PostgreSQL Global Development Group: „PostgrSQL 7.4.1 Documentation”, www.postgresql.org, S. 209
[16] TPC Benchmark H: „Standard Specification, Revision 2.1.0” www.tpc.org, S. 6
[17] A. Kemper, D. Kossmann, B. Zeller: „Performance Tuning for SAP R/3”, IEEE
Data Engineering Bulletin, S. 2, 1999
2004-09-30/101/IN99/2254
Herunterladen