pdf (870 Kb) - Fachgebiet Datenbanken und Informationssysteme
pdf (870 Kb) - Fachgebiet Datenbanken und Informationssysteme
pdf (870 Kb) - Fachgebiet Datenbanken und Informationssysteme
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
Universität Hannover<br />
Fachbereich Informatik<br />
Institut für <strong>Informationssysteme</strong><br />
Diplomarbeit<br />
Heuristische Anfrageoptimierungen<br />
in Relationalen <strong>Datenbanken</strong><br />
16. Januar 2003<br />
Mazeyar E. Makoui<br />
Matrikel-Nr. 1873773<br />
Erstprüfer: Prof. Dr. Udo Lipeck<br />
Zweitprüfer: Prof. Dr. Rainer Parchmann
Erklärung<br />
Hiermit versichere ich, Mazeyar E. Makoui, die vorliegende Diplomarbeit ohne<br />
fremde Hilfe <strong>und</strong> nur unter Verwendung der von mir aufgeführten Quellen <strong>und</strong><br />
Hilfsmittel angefertigt zu haben.<br />
Hannover, 16. Januar 2003.<br />
(Mazeyar E. Makoui)
Es gibt nur zwei Dinge, die unendlich groß sind:<br />
Das Weltall <strong>und</strong> die Dummheit des Menschen.<br />
Beim Weltall bin ich mir nicht sicher.<br />
Es gibt nur zwei Dinge, die in unserer Arbeit von nöten sind:<br />
Unermüdliche Ausdauer <strong>und</strong> die Bereitschaft,<br />
etwas, in das man viel Zeit <strong>und</strong> Arbeit gesteckt hat,<br />
wieder wegzuwerfen.<br />
Was wirklich zählt, ist die Intuition.<br />
Albert Einstein
Danksagung<br />
An dieser Stelle möchte ich die Gelegenheit nutzen <strong>und</strong> mich bei denen bedanken,<br />
die mich im Laufe der Zeit auf meinem Weg begleiteten <strong>und</strong> somit erst diese<br />
Diplomarbeit ermöglichten.<br />
Besonderer Dank geht an Herrn Prof. Dr. Udo Lipeck, dem ich viele St<strong>und</strong>en der<br />
kreativen Diskussion <strong>und</strong> kritischen Auseinandersetzung verdanke. Erst durch<br />
sein Vertrauen in meine Person konnte ich diese Diplomarbeit angehen <strong>und</strong> zu<br />
einem Abschluß bringen. Mein Dank geht auch an Prof. Dr. Rainer Parchmann<br />
für seinen Einsatz als Koreferent.<br />
Auch Herrn Prof. Dr. Bernhard Seeger von der Universität Marburg möchte<br />
ich ganz herzlich danken. Er gewährte mir den Zugang zu seinen Unterlagen,<br />
bezüglich der von ihm gehaltenen Vorlesung über Anfrageoptimierung.<br />
Desweiteren möchte ich Katharina Ludwig für ihre kritischen Bemerkungen danken,<br />
die mich immer wieder daran erinnerten, daß meine Gedanken <strong>und</strong> Ideen<br />
auch aufgeschrieben werden müssen. Herr Dipl.-Math. Frank Samir Attia ließ<br />
mich die Dinge oft aus einem anderen Blickwinkel neu betrachten. Außerdem<br />
Anika Höptner, die mich seelisch <strong>und</strong> fre<strong>und</strong>schaftlich in den letzten Jahren begleitete.<br />
Hervorheben möchte ich folgende Fre<strong>und</strong>e: Dipl.-Math. Anke Eberhard, cand.<br />
math. Cord Erdenberger, Dr. rer. nat. Daniel Frohn, Dipl.-Math. oec. Marc Heitzer,<br />
cand. math. Iris Lieske, Dipl.-Dolm. Manfred Lisius, Dipl.-Math. SR-Inf. Ulf<br />
Löckmann, Dipl.-Math. Marcus Reich, Dipl.-Math. Simon Stelling, cand. math.<br />
Matthias Schütt <strong>und</strong> Christian Wederhake.<br />
Schließlich möchte ich meinen Eltern herzlich danken, die vieles auf sich nahmen,<br />
um uns Kindern die Möglichkeit zu geben, in einem fremden Land, all das zu<br />
erreichen, was wir uns schon immer gewünscht hatten.<br />
Hannover, am 16. Januar 2003.
Zusammenfassung<br />
Das Ziel der Anfrageverarbeitung in Relationalen <strong>Datenbanken</strong> besteht darin, für<br />
eine gegebene Anfrage den optimalen Ausführungsplan zu generieren. Da dieses<br />
im allgemeinen sehr aufwendig ist, reduziert man in der Praxis die Anforderungen<br />
darauf, schlechte Ausführungspläne zu vermeiden.<br />
In der vorliegenden Diplomarbeit wird zunächst eine Einführung in dieses komplexe<br />
Thema gegeben, um danach die heuristische Anfrageoptimierung näher zu<br />
untersuchen. Sie beinhaltet als Basis ein Kostenmodell, das es einem ermöglicht,<br />
relative Aussagen über die Güte des optimierten Anfrageplans zu tätigen. Dabei<br />
ist nicht die Exaktheit der Kosten wichtig, sondern die relativen Kostendifferenzen<br />
der jeweiligen Anfragepläne.<br />
Diese Alternativen entstehen in zwei aufeinanderfolgenden Phasen der Optimierung.<br />
Zuerst wird eine algebraische Optimierung vorgenommen, bei der durch<br />
Termersetzungsregeln Äquivalenzumformungen durchgeführt werden. Da für einen<br />
Operator der logischen Algebra oft unterschiedliche Implementierungen existieren,<br />
wird in der darauffolgenden physischen Optimierung eine möglichst kostengünstigste<br />
gewählt. In beiden Fällen werden Heuristiken zur Steuerung der<br />
Alternativengenerierung eingesetzt. Sie repräsentieren Erfahrungswerte für die<br />
sinnvolle Anwendung bestimmter Umformungsregeln.<br />
Die für die Anfrageoptimierung benötigten Suchverfahren bieten anschließend<br />
die Möglichkeit, den entwickelten Anfragebaum teilweise noch zu verbessern. Die<br />
dafür gegebenen Zusatzinformationen erlauben die Nutzung einer Auswahl von<br />
Suchverfahren, die eine kostengünstigere - wiederum durch Heuristiken beschleunigte<br />
- Anfrage finden können.<br />
Zum Abschluß der Arbeit wird der vorgestellte Anfrageoptimierer simuliert, um<br />
nähere Untersuchungen bezüglich der Feinabstimmungen zu ermöglichen. Dabei<br />
wird das Hauptaugenmerk auf die notwendige Flexibilität <strong>und</strong> die Erweiterbarkeit<br />
der programmierten Optimierungsmodule gelenkt.
Abstract<br />
The aim of the query evaluation in relational data base systems consists of generating<br />
the optimal execution plan for a given query. Since this is generally a<br />
very complex matter, one reduces the requirements in practice to avoid bad query<br />
trees.<br />
This diploma presents an introduction to this complex topic, in order to examine<br />
afterwards the heuristic query optimization closer. By giving a basic cost model,<br />
it gives one the possibility, to make relative statements about the quality of the<br />
optimized query plan. Not the exactness of the costs is important, but the relative<br />
cost differences of the respective query plans.<br />
These alternatives are developed in two following phases of the optimization.<br />
First an algebraic optimization is made, with which equivalence rewritings are<br />
accomplished by transformation rules. Since for a logical algebra operator often<br />
different implementations exist, the most economical plan is selected in the following<br />
physical optimization. In both cases heuristics are used for the controlling<br />
of generating alternatives. They represent empirical values for the meaningful<br />
application of certain rewriting rules.<br />
The search methods needed for the query optimization offer afterwards the possibility<br />
of improving the developed query tree. The additional given metadatas<br />
permit to use a selection of search methods, which can find a more economical -<br />
again accelerated by heuristics - query.<br />
For the conclusion of this work the presented query optimizer is simulated, in<br />
order to make closer investigations possible concerning the fine tunings. Special<br />
attention is directed on the necessary flexibility and the expandability of the<br />
programmed optimization modules.
Inhaltsverzeichnis<br />
1 Einleitung 1<br />
1.1 Ziel dieser Diplomarbeit . . . . . . . . . . . . . . . . . . . . . . . 6<br />
1.2 Aufbau dieser Diplomarbeit . . . . . . . . . . . . . . . . . . . . . 6<br />
2 Algebraische Optimierung 9<br />
2.1 Rechenregeln der relationalen Algebra . . . . . . . . . . . . . . . . 10<br />
2.2 Beispiel für die algebraische Optimierung . . . . . . . . . . . . . . 14<br />
2.3 Heuristische algebraische Optimierung . . . . . . . . . . . . . . . 17<br />
2.4 Tableau-Optimierung . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
2.5 Fazit der algebraischen Optimierung . . . . . . . . . . . . . . . . 18<br />
3 Physische Optimierung 21<br />
3.1 Iteratoren <strong>und</strong> Iteratorbäume . . . . . . . . . . . . . . . . . . . . 22<br />
3.2 Scan-Methoden . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.2.1 Projektion . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.2.2 Selektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.3 Join-Methoden (✶p, ⊲⊳p) . . . . . . . . . . . . . . . . . . . . . . . 25<br />
3.3.1 NestedLoop-Join (✶ NestedLoop<br />
p ) . . . . . . . . . . . . . . . . 26<br />
3.3.2 Index-Join (✶ Index<br />
p ) . . . . . . . . . . . . . . . . . . . . . . 27<br />
3.3.3 Merge-Join (✶ Merge<br />
p ) . . . . . . . . . . . . . . . . . . . . . 28<br />
3.3.4 Hash-Join (✶ Hash<br />
p ) . . . . . . . . . . . . . . . . . . . . . . 29<br />
3.3.5 Semijoin (⋉p, ⋊p) - Antisemijoin ( ¯⋉p) . . . . . . . . . . . . 30<br />
3.3.6 Outer-Joins (❂⋊p, ⋉❁p, ❂×❁p) . . . . . . . . . . . . . . . . . 31<br />
3.4 Mengenoperatoren . . . . . . . . . . . . . . . . . . . . . . . . . . 32<br />
3.5 Sortierung <strong>und</strong> Duplikateliminierung . . . . . . . . . . . . . . . . 33<br />
3.6 Übersetzung der logischen Algebra . . . . . . . . . . . . . . . . . 34<br />
3.7 Physische Optimierungsmöglichkeiten . . . . . . . . . . . . . . . . 35<br />
3.8 Heuristische physische Optimierung . . . . . . . . . . . . . . . . . 37<br />
3.9 Beispiel für die physische Optimierung . . . . . . . . . . . . . . . 39<br />
3.10 Fazit der physischen Optimierung . . . . . . . . . . . . . . . . . . 40<br />
i
ii INHALTSVERZEICHNIS<br />
4 Kostenfunktionen <strong>und</strong> Selektivitäten 41<br />
4.1 Allgemeine Kostenberechnung . . . . . . . . . . . . . . . . . . . . 42<br />
4.2 Kostenfunktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />
4.3 Selektivität von Prädikaten . . . . . . . . . . . . . . . . . . . . . 45<br />
4.3.1 Selektivität von Projektion <strong>und</strong> Selektion . . . . . . . . . . 46<br />
4.3.2 Selektivität eines Joins . . . . . . . . . . . . . . . . . . . . 49<br />
4.4 Selektivitätsabschätzungen . . . . . . . . . . . . . . . . . . . . . . 53<br />
4.4.1 Parametrisierte Verteilung . . . . . . . . . . . . . . . . . . 53<br />
4.4.2 Histogramme . . . . . . . . . . . . . . . . . . . . . . . . . 54<br />
4.4.3 Stichproben . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />
4.4.4 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . 55<br />
4.5 Fazit der Selektivitätsabschätzungen . . . . . . . . . . . . . . . . 55<br />
4.6 Kostenfunktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />
4.6.1 Kostenfunktion Scan (Selektion, Projektion) . . . . . . . . 57<br />
4.6.2 Kostenfunktion Sortierung <strong>und</strong> Indexerzeugung . . . . . . 58<br />
4.6.3 Kostenfunktion Join . . . . . . . . . . . . . . . . . . . . . 59<br />
4.7 Kosteneinsparungen beim Pipelining . . . . . . . . . . . . . . . . 60<br />
4.8 Beispiel zur Kostenberechnung . . . . . . . . . . . . . . . . . . . . 60<br />
4.9 Fazit des Kostenmodells . . . . . . . . . . . . . . . . . . . . . . . 65<br />
5 Kostenbasierte Optimierung 67<br />
5.1 Heuristische Suchverfahren . . . . . . . . . . . . . . . . . . . . . . 68<br />
5.1.1 Hill-Climbing Suche . . . . . . . . . . . . . . . . . . . . . . 68<br />
5.1.2 Simulated Annealing . . . . . . . . . . . . . . . . . . . . . 69<br />
5.1.3 Best-First Suche . . . . . . . . . . . . . . . . . . . . . . . 70<br />
5.1.4 Beam-Search . . . . . . . . . . . . . . . . . . . . . . . . . 71<br />
5.2 Fazit der heuristischen Suche . . . . . . . . . . . . . . . . . . . . . 71<br />
5.3 Genetische Algorithmen . . . . . . . . . . . . . . . . . . . . . . . 71<br />
6 Die Optimierungsstrategie 73<br />
6.1 Transformation <strong>und</strong> Generierung . . . . . . . . . . . . . . . . . . 76<br />
6.2 Die globale kostenbasierte Optimierungsheuristik . . . . . . . . . 80<br />
6.3 Die lokale kostenbasierte Optimierungsheuristik . . . . . . . . . . 84<br />
6.4 Fazit der kostenbasierten Optimierung . . . . . . . . . . . . . . . 85<br />
6.5 Beispiel zur kostenbasierten Suche . . . . . . . . . . . . . . . . . . 86<br />
7 Die Implementierung des SQL-Optimierers 91<br />
7.1 Die Packages des SQL-Optimierers . . . . . . . . . . . . . . . . . 91<br />
7.2 Grafische Benutzerschnittstelle GUI . . . . . . . . . . . . . . . . . 93<br />
7.2.1 de.unihannover.dbs.sopt.gui . . . . . . . . . . . . . . . . . 93<br />
7.3 Der Relationale Anfrageparser . . . . . . . . . . . . . . . . . . . . 96<br />
7.3.1 de.unihannover.dbs.sopt.sqlp . . . . . . . . . . . . . . . . . 96<br />
7.4 Datenstruktur des Anfrageoptimierers . . . . . . . . . . . . . . . . 98
INHALTSVERZEICHNIS iii<br />
7.4.1 de.unihannover.dbs.sopt.struc.alg . . . . . . . . . . . . . . 98<br />
7.4.2 de.unihannover.dbs.sopt.struc.alg.rules . . . . . . . . . . . 101<br />
7.4.3 de.unihannover.dbs.sopt.struc.phys . . . . . . . . . . . . . 101<br />
7.4.4 de.unihannover.dbs.sopt.struc.search . . . . . . . . . . . . 104<br />
7.5 Die Testschnittstelle . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />
7.5.1 de.unihannover.dbs.sopt.test . . . . . . . . . . . . . . . . . 105<br />
7.6 Benutzerhandbuch . . . . . . . . . . . . . . . . . . . . . . . . . . 105<br />
7.6.1 Starten des SQL-Optimierers . . . . . . . . . . . . . . . . 105<br />
8 Ausblick 109<br />
A Der Oracle Optimierer 111<br />
A.1 Rule based optimizer . . . . . . . . . . . . . . . . . . . . . . . . . 111<br />
A.2 Cost based optimizer . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />
A.3 Cost based optimizer mit hints . . . . . . . . . . . . . . . . . . . . 113<br />
A.4 Statistiken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113<br />
A.5 Standardeinstellungen des Optimierers . . . . . . . . . . . . . . . 114<br />
A.6 Indexe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115<br />
B Konventionen dieser Arbeit 117<br />
C Weblinks zu dieser Diplomarbeit 119<br />
Abbildungsverzeichnis 123<br />
Tabellenverzeichnis 125<br />
Literaturverzeichnis 127
Kapitel 1<br />
Einleitung<br />
Wenn ich die Folgen geahnt hätte,<br />
wäre ich Uhrmacher geworden.<br />
Albert Einstein<br />
Anfragen in deklarativen Sprachen wie SQL, die an ein Datenbanksystem gestellt<br />
werden, definieren zwar das, was der Benutzer wissen möchte, jedoch nicht, wie<br />
das System die gewünschte Information ermitteln soll. Daraus resultieren zumeist<br />
viele verschiedene Möglichkeiten, eine Anfrage auszuwerten. Aus diesem Gr<strong>und</strong><br />
sollte möglichst früh eine Anfrageoptimierung bei der Anfragebearbeitung stattfinden.<br />
Anfrageoptimierung<br />
❄ ❄<br />
regelbasierte Verfahren kostenbasierte Verfahren<br />
❄<br />
Branch-and-Bo<strong>und</strong> Greedy Methode<br />
❄<br />
❄<br />
zufallsorientierte Verfahren<br />
❄<br />
Optimierer Generator<br />
dynamische Programmierungsmethode<br />
Abbildung 1.1: Strategien zur Anfrageoptimierung<br />
Dafür wird bezüglich der Auswahl realisierter Ausführungspläne zwischen kostenbasierter<br />
<strong>und</strong> regelbasierter Anfrageoptimierung unterschieden. Während bei<br />
der kostenbasierten Anfrageoptimierung Ausführungspläne aufgr<strong>und</strong> eines Gütekriteriums<br />
ausgewertet werden, erfolgt bei der regelbasierten die Überprüfung<br />
alternativer Ausführungspläne anhand einer Menge von Regeln.<br />
Kostenbasierte Verfahren sind zum Beispiel genetische Anfrageoptimierung [54],<br />
die zu den zufallsorientierten Verfahren gehören, oder aber auch die dynamische<br />
Programmierung, wie sie im System R Optimierer 1 eingesetzt wird. Zusätzlich<br />
1 Auf Basis des 1979 verfaßten Artikels von Selinger [51] bei IBM entwickelter Optimierer<br />
für Relationale <strong>Datenbanken</strong>.<br />
1<br />
❄
2 KAPITEL 1. EINLEITUNG<br />
dazu gibt es noch Greedy-Algorithmen, bei denen eine schrittweise Zerlegung der<br />
Anfrage heuristisch vorgenommen wird.<br />
Gegenstand dieser Arbeit ist die Entwicklung eines Optimierer-Generators (regelbasierter<br />
Algorithmus), der aus der Menge der verschiedenen, jedoch semantisch<br />
äquivalenten Auswertungspläne, dem Anfrageraum, den Plan auswählt, der mit<br />
den geringsten Kosten verb<strong>und</strong>en ist, d.h. mit dem heuristisch kleinsten Zeitaufwand<br />
ausgeführt werden kann. Da für den Anwender entscheidend ist, wieviel Zeit<br />
das Datenbanksystem von der Eingabe der Anfrage bis zur Ausgabe des Ergebnisses<br />
benötigt, muß dabei auch beachtet werden, wie zeitintensiv die Optimierung<br />
ist. Ein Algorithmus, der stets den Plan mit der kürzesten Ausführungszeit findet,<br />
dafür aber mehr Zeit benötigt als durch die Optimierung eingespart wird,<br />
ist für den Anwender sicherlich nicht akzeptabel.<br />
Verbessert man den Optimierer dahingegend, daß er nach den vorgegebenen Regeln<br />
noch eine weitere Suche vornimmt, erreicht man die sogenannten Branchand-Bo<strong>und</strong><br />
Suchverfahren, die von hier an als Kostenbasierte Optimierung bezeichnet<br />
werden.<br />
Der Ablauf der Anfragebearbeitung sieht wie folgt aus:<br />
deklarative Anfrage<br />
❄<br />
Scanner / Parser / View-Resolution<br />
algebraischer Ausdruck<br />
❄<br />
Anfrageoptimierung<br />
Auswertungsplan (QEP)<br />
❄<br />
Codeerzeugung / Ausführung<br />
Ergebnis<br />
❄<br />
algebraische Optimierung<br />
✒✒✒ algebraischer Ausdruck<br />
✒ ❄<br />
physische Optimierung<br />
❘ ❘❘❘<br />
mehrere Zugriffspläne<br />
❄<br />
kostenbasierte Optimierung<br />
Abbildung 1.2: Ablauf der Anfrageoptimierung<br />
Die vom Benutzer eingegebene Anfrage wird im ersten Schritt von einem Parser<br />
in eine interne Form gebracht, die dann unter Zuhilfenahme des Data Dictionaries<br />
validiert wird, um sicherzustellen, daß die Anfrage nur erlaubte <strong>und</strong> gültige<br />
Referenzen auf existierende Datenbank-Objekte enthält. Wenn Makros benutzt<br />
worden sind, werden sie durch ihre Definitionen ersetzt.<br />
Der daraus resultierende algebraische Ausdruck wird dann einer Anfrageoptimierung<br />
unterzogen, die sich in algebraische, physische <strong>und</strong> kostenbasierte Optimierung<br />
aufteilt, wobei die Übergänge in der Implementierung fließend sind. Schließ-
lich wird der optimale Ausführungsplan (Query-Evaluation-Plan oder Query-<br />
Execution-Plan, kurz QEP) in eine ausführbare Form gebracht, so daß - nach<br />
der Bearbeitung - dem Benutzer das Ergebnis präsentiert wird.<br />
Die algebraische Optimierung besteht darin, daß später vorgestellte Rechenregeln<br />
der Relationen-Algebra angewandt werden. Unter Umständen können noch<br />
weitere Verbesserungen vorgenommen werden. Dabei werden die tatsächlichen<br />
Kenngrößen der Relationen sowie das Speichermodell noch nicht berücksichtigt.<br />
Danach erfolgt die physische Optimierung unter Zuhilfenahme von konkreten<br />
Speicherungs- <strong>und</strong> Zugriffstechniken (z.B. Indexe <strong>und</strong> Cluster) <strong>und</strong> Nutzung von<br />
Implementierungsalternativen für algebraische Operationen.<br />
Am Ende dieser Phase werden mehrere semantisch äquivalente Zugriffspläne weitergegeben,<br />
die einen geringen Aufwand erwarten lassen. Diese Zugriffspläne werden<br />
bei der kostenbasierten Optimierung mit Hilfe von Kostenabschätzungen bewertet<br />
<strong>und</strong> eventuell noch modifiziert. Danach wird der optimale Plan gewählt.<br />
Darüberhinaus gibt es für die Relationenkalküle oder für die Sprache Datalog<br />
(Modellsprache Deduktiver <strong>Datenbanken</strong>, Relationen werden als Faktenmenge<br />
betrachtet) heuristische Optimierungstechniken, die unabhängig von algebraischer<br />
Transformation agieren.<br />
Bei der Optimierung von Anfragen in klassischen relationalen Systemen lassen<br />
sich zwei Problemkreise identifizieren:<br />
(a) Zum einen muß in der algebraischen <strong>und</strong> physischen Optimierung aus der<br />
Menge aller möglichen Auswertungspläne eine möglichst kleine Anzahl von<br />
Plänen extrahiert werden, die geringe Kosten bei der Auswertung versprechen,<br />
aber möglichst ohne dabei sehr gute Pläne auszulassen;<br />
(b) zum anderen müssen für die kostenbasierte Optimierung Verfahren entwickelt<br />
werden, mit deren Hilfe die Auswertungskosten eines Planes möglichst<br />
exakt geschätzt werden können, ohne die Anfrage tatsächlich auszuführen.<br />
Dabei gibt es drei verschiedene Zeitpunkte, an denen die Optimierungen stattfinden<br />
können:<br />
(a) Statisch:<br />
Hierbei wird die Optimierung vor der Ausführung getätigt. Das ist sinnvoll,<br />
wenn die Kostenabschätzungen relativ scharf sind <strong>und</strong> damit die Bewertung<br />
der verschiedenen Pläne sehr genau ist. Anderenfalls ist das Risiko, einen<br />
der schlechteren Pläne zu wählen, recht hoch.<br />
3
4 KAPITEL 1. EINLEITUNG<br />
(b) Dynamisch:<br />
In diesem Fall wird ” während“ der Ausführung optimiert. Da dabei die<br />
Zwischenergebnisse vorliegen, kann eine Entscheidung auf einer sicheren<br />
Gr<strong>und</strong>lage vorgenommen werden. Der Schwachpunkt ist, daß das Verwerfen<br />
einer begonnenen, als suboptimal erkannten Ausführung sehr hohe Kosten<br />
erzeugt.<br />
(c) Hybrid:<br />
Dieser Nachteil kann durch einen kombinierten Einsatz der beiden obigen<br />
Verfahren vermieden werden. Vor der Ausführung wird mit Schätzungen<br />
ein möglichst optimaler Plan ausgewählt, wobei Korrekturmöglichkeiten zur<br />
Laufzeit möglich sind, wenn tatsächliche Größen zu sehr von den Schätzungen<br />
abweichen.<br />
Dabei gibt es zwei Abstraktionslevel der Optimierung, die meist in der Literatur<br />
konzeptionell benutzt werden, während praktisch eine generelle, strikte Trennung<br />
der einzelnen Phasen in der Implementierung eines Optimierers gar nicht stattfindet:<br />
algebraische Optimierung<br />
algebraischer Ausdruck<br />
❄<br />
physische Optimierung ✛<br />
mehrere Zugriffspläne<br />
❄<br />
kostenbasierte Optimierung ✛<br />
✛ High-Level-Optimierung<br />
Abbildung 1.3: Phasen der Optimierung<br />
Low-Level-Optimierung<br />
(a) Eine Anfrage kann alleine durch ” Rechenregeln“ verbessert werden, welche<br />
für die Operatoren der betreffenden Hochsprache gelten. Da dabei<br />
nur im abstrakten Raum agiert wird, spricht man hierbei von einer High-<br />
Level-Optimierung, diese ist datenbankunabhängig. Es kommen hierbei nur<br />
sogenannte Termersetzungs-Techniken zur Anwendung, die auf syntaktischer<br />
Ebene Umformungen an einer Anfrage erlauben <strong>und</strong> somit eine Art<br />
Anfrage-Modifikation darstellen.<br />
Es sei an dieser Stelle auch bemerkt, daß eine High-Level-Optimierung bereits<br />
auf der Ebene einer deklarativen Sprache ansetzen kann.<br />
(b) Dagegen wird von einer Low-Level-Optimierung ausgegangen, wenn Parameter<br />
der internen Datenbank-Organisation (Data-Dictionary bzw. System-
katalog), in die Optimierung einbezogen werden. Unterschieden wird hierbei,<br />
ob ” nur“ Metadaten oder auch der konkrete Datenbestand eingehen.<br />
Schließlich existieren zwei verschiedene Optimierungsstrategien:<br />
(a) Vollständige Suche (enumerative Suche):<br />
Dabei werden alle möglichen Anfragepläne generiert <strong>und</strong> bewertet (”Generate-and-Test”-<br />
Strategie). Diese Optimierung ist meist zu teuer <strong>und</strong> nur<br />
dann sinnvoll, wenn der Anfrageraum relativ klein ist, z.B. weil nur sehr wenige<br />
Operationen nötig <strong>und</strong> damit wenige Umformungen möglich sind. Beispiele<br />
dafür sind die Tiefensuche (depth-first search) oder die Breitensuche<br />
(breadth-first search). Da ohnehin alle Möglichkeiten untersucht werden, ist<br />
es hierbei unerheblich, wo die Suche begonnen wird.<br />
Bei heutigen föderalen Datenbanksystemen ist die vollständige Suche einfacher<br />
Anfragen aber immer öfter die bessere Alternative, da Datenbankzugriffe<br />
bei breitverteilten Systemen kostenintensiver sein können als zeitintensive<br />
Optimierungen.<br />
(b) Heuristische Suche:<br />
Bei der heuristischen Suche versucht man nun die Optimierungszeit zu verringern,<br />
indem man wahrscheinlich ungünstige Pläne im voraus ausschließt.<br />
Hier ist es wichtig, für die Suche schon einen möglichst guten Startplan zu<br />
kennen, z.B. kann man versuchen, die Größe der Zwischenrelationen zu<br />
verkleinern. Hierfür sind die im Abschnitt 2.3 beschriebenen heuristischen<br />
Regeln zur Transformation eines Anfragebaumes der entscheidende Schritt.<br />
Ausgehend von einem relativ guten Startplan sind dann die heuristischen<br />
Suchverfahren wie z.B. Greedy-Suche, Best-First-Suche <strong>und</strong> Hill-Climbing<br />
(Kapitel 5) in der Lage, in kürzerer Zeit einen optimierten Anfrageplan zu<br />
finden.<br />
Für die heuristische Suche müssen nun Regeln gef<strong>und</strong>en werden, die den Suchraum<br />
möglichst stark einschränken, aber gleichzeitig das Risiko klein halten, einen<br />
optimalen Plan auszuschließen. In der algebraischen <strong>und</strong> physischen Optimierung<br />
soll die Heuristik an zwei Punkten ansetzen. Ausgehend von dem übergebenen<br />
algebraischen Ausdruck soll sie erstens Erfolg versprechende Transformationen<br />
schnell finden <strong>und</strong> zweitens (höchstwahrscheinlich) wenig sinnvolle Umformungen<br />
ausschließen.<br />
5
6 KAPITEL 1. EINLEITUNG<br />
1.1 Ziel dieser Diplomarbeit<br />
Das Ziel dieser Diplomarbeit ist es, nach einer Einführung in die algebraische bzw.<br />
physische Optimierung einen selbstentwickelten Optimierungssimulator vorzustellen,<br />
der in der Lage ist, die entwickelten Optimierungsstrategien auszuführen.<br />
Die Hauptidee für die heuristische Suche ist hierbei, zu einer gegebenen Anfrage<br />
einen sogenannten Prototypen im Anfrageraum zu finden <strong>und</strong> von dort aus die<br />
Suche fortzusetzen. Wichtig für den Erfolg dieser Strategie ist, daß der Prototyp<br />
bereits in der Nähe des absoluten Minimums der Kosten liegt.<br />
✛<br />
❅■<br />
❅■<br />
❅<br />
✻<br />
❜ �<br />
�✒<br />
✻<br />
Prototyp<br />
1:1-Übersetzung<br />
Anfrageraum<br />
Abbildung 1.4: Anfrageraum der Ausführungspläne<br />
Der Prototyp entwickelt sich aus der 1:1-Übersetzung der Anfrage in einen Baum<br />
<strong>und</strong> darauf angewandten Faustregeln. Ausgehend von diesem Prototypen können<br />
dann - durch weitere Modifikation des Operatorbaumes, unter Zuhilfenahme der<br />
Kostenfunktion - weitere Anfragepläne generiert <strong>und</strong> verglichen werden.<br />
1.2 Aufbau dieser Diplomarbeit<br />
Die folgenden Kapitel gliedern sich wie folgt:<br />
Im 1. Kapitel wird dem Leser eine kurze Einführung in das Thema der Anfrageoptimierung<br />
geboten. Danach folgt in Kapitel 2 eine Darstellung der Ersetzungsregeln<br />
der Relationenalgebra, die daraufhin als Gr<strong>und</strong>lage für Anfrageheuristiken<br />
dient.<br />
Kapitel 3 schafft den Übergang von den algebraischen Operatoren hin zur physischen<br />
Implementierung einer Anfrage.<br />
Das wahrscheinlich wichtigste Kapitel ist das vierte. Hier werden die Kostenfunktionen<br />
zu den einzelnen Operatoren aufgestellt, die für jede Art von (kostenbasierter)<br />
Anfrageoptimierung benötigt werden.
1.2. AUFBAU DIESER DIPLOMARBEIT 7<br />
In Kapitel 5 werden die vorher erzielten Ergebnisse angewandt, um die kostenbasierte<br />
Optimierung zu erläutern.<br />
Das Herzstück der Diplomarbeit ist das 6. Kapitel, das die Entwicklung <strong>und</strong> die<br />
theoretische Gr<strong>und</strong>lage des SQL-Optimierers erklären wird. Anschließend wird<br />
eine Bewertung der betrachteten Optimierungsmethoden vorgenommen. Danach<br />
folgt ein Ausblick auf weitere Optimierungsstrategien.<br />
In Kapitel 7 wird der simulierte Anfrage-Optimierer näher erklärt. Dieses Kapitel<br />
beschäftigt sich mit dem Implementierungsanteil dieser Diplomarbeit. 2<br />
Schließlich setzt Kapitel 8 einen Schlußstrich unter diese Arbeit <strong>und</strong> gibt außerdem<br />
einen Ausblick.<br />
2 Als Referenz für die Implementierung des Anfragesimulators dienen die Möglichkeiten des<br />
Oracle-Optimierers, die im Anhang A dargestellt werden.
8 KAPITEL 1. EINLEITUNG
Kapitel 2<br />
Das Schönste, was wir erleben können,<br />
ist das Geheimnisvolle.<br />
Albert Einstein<br />
Algebraische Optimierung<br />
Im Rahmen der algebraischen Optimierung wird die interne 1:1-Übersetzung<br />
der Anfrage in eine Standardform transformiert, die zwar noch äquivalent zur<br />
Original-Anfrage ist, aber schon eine gewisse Optimierung beinhaltet. Dabei wird<br />
eine gr<strong>und</strong>sätzliche Form benötigt, bei der die Ausdrücke der relationalen Algebra<br />
sich direkt in Operatorbäume umsetzen lassen. Relationale Algebraausdrücke<br />
<strong>und</strong> somit auch die Operatorbäume spiegeln jeweils eine prozedurale Darstellung<br />
der Anfrage wider. Die Blätter sind die Operanden des algebraischen Ausdrucks,<br />
wobei die inneren Knoten die Basisrelationen bzw. Operationen darstellen <strong>und</strong><br />
an der Wurzel die letzte auszuführende Operation steht.<br />
Blätter: die Operanden innere Knoten: die Operationen<br />
Kanten: der Datenfluß Wurzel: die letzte auszuführende Operation<br />
Diese baumartige Darstellung kann nicht nur als Evaluierungsvorschrift angesehen<br />
werden, sondern dient hauptsächlich als effiziente Datenstruktur. Das Einfügen<br />
neuer Operatoren ist sehr einfach, da eine Erweiterung des Baumes durch<br />
Einschübe von Teilbäumen leicht implementiert werden kann.<br />
Die dafür benötigte Anfrageumformung (1:1-Übersetzung) wird mit wenig komplizierten<br />
Methoden realisiert. Dabei wird die Anfrage normalisiert <strong>und</strong> vereinfacht,<br />
wobei noch keine Optimierung im eigentlichen Sinne vorgenommen wird.<br />
Vielmehr versucht man, die Anfrage möglichst gut für den Optimierer vorzubereiten.<br />
Dabei wird vor allem von aussagelogischen Methoden Gebrauch gemacht<br />
(siehe dazu Mitschang [36]).<br />
Um möglichst früh Inkonsistenzen <strong>und</strong> unerfüllbare Anfragen zu erkennen, kann<br />
bereits in der Übersetzungsphase eine Vereinfachung der Anfrage erfolgen. Meistens<br />
werden diese aber nach der 1:1-Übersetzung der Selektionsbedingungen<br />
durchgeführt, da erst dann eine effiziente Untersuchung der aussagelogischen Ausdrücke<br />
möglich ist.<br />
9
10 KAPITEL 2. ALGEBRAISCHE OPTIMIERUNG<br />
Alle möglichen Vereinfachungsoperationen bzw. Termersetzungen werden nicht<br />
sequentiell durchgeführt, sondern in einem iterativen Prozeß immer wieder eingesetzt.<br />
Dafür benötigt man eine Vielzahl von Regeln, die im folgenden Abschnitt<br />
näher beschrieben werden.<br />
2.1 Rechenregeln der relationalen Algebra<br />
Für die Optimierung der relationalen Ausdrücke benötigt man Ersetzungsregeln,<br />
die einem die Äquivalenzumformungen ermöglichen:<br />
Abkürzungen:<br />
R, R1, R2, . . . Relationen (z. B. Zwischenergebnisse)<br />
q, p, p0, p1, p2, . . . Bedingungen<br />
l1, l2, . . . Attributmengen<br />
attr(p) die Menge der in p enthaltenen Attribute<br />
sch(R) das Schema (also die Menge der Attribute) der Relation R<br />
”→”, ”←” Hin- bzw. Rückrichtungsbedingung<br />
einer Gleichheitsaussage<br />
Operationen:<br />
∩ Durchschnitt zweier Relationen<br />
∪ Vereinigung zweier Relationen<br />
− Differenz zweier Relationen<br />
× Kartesisches Produkt zweier Relationen<br />
πl Projektion einer Relation auf die Attributmenge l<br />
σp Selektion einer Relation durch die Bedingung p<br />
✶p Verb<strong>und</strong> zweier Relationen durch die Bedingung p<br />
Dafür gelten folgende Gesetze:<br />
(a) R ∪ R = R<br />
R − R = ∅<br />
(b) σp1(R) ∪ σp2(R) = σp1∨p2(R)<br />
σp1(R) − σp2(R) = σp1∧¬p2(R)<br />
(c) R ∪ ∅ = R<br />
∅ ∪ R = R<br />
R − ∅ = R<br />
∅ − R = ∅<br />
R × ∅ = ∅<br />
∅ × R = ∅
2.1. RECHENREGELN DER RELATIONALEN ALGEBRA 11<br />
(d) σp(∅) = ∅<br />
πl(∅) = ∅<br />
(e) πsch(R)(R) = R<br />
(f) Join, Vereinigung, Schnitt <strong>und</strong> das Kreuzprodukt sind kommutativ:<br />
R1 ✶ R2 = R2 ✶ R1<br />
R1 ∪ R2 = R2 ∪ R1<br />
R1 ∩ R2 = R2 ∩ R1<br />
R1 × R2 = R2 × R1<br />
(g) Join, Vereinigung, Schnitt <strong>und</strong> das Kreuzprodukt sind assoziativ:<br />
R1 ✶ (R2 ✶ R3) = (R1 ✶ R2) ✶ R3 = R1 ✶ R2 ✶ R3<br />
R1 ∪ (R2 ∪ R3) = (R1 ∪ R2) ∪ R3 = R1 ∪ R2 ∪ R3<br />
R1 ∩ (R2 ∩ R3) = (R1 ∩ R2) ∩ R3 = R1 ∩ R2 ∩ R3<br />
R1 × (R2 × R3) = (R1 × R2) × R3 = R1 × R2 × R3<br />
(h) Selektionen sind untereinander vertauschbar:<br />
σp(σq(R)) = σq(σp(R))<br />
Eine Optimierung der Anfrage wird hierbei durch die Wahl der Restriktivsten<br />
zur inneren Selektion erreicht.<br />
(i) Alle Konjunktionen in einer Selektionsbedingung können in mehrere Selektionen<br />
aufgebrochen, bzw. nacheinander auszuführende Selektionen können<br />
durch Konjunktionen zusammengefügt werden:<br />
σp1∧p2∧...∧pn(R) = σp1(σp2(. . . (σpn(R)) . . .))<br />
(j) Alle geschachtelten Projektionen können eliminiert werden:<br />
πl1(πl2(. . . (πln(R)) . . .)) = πl1(R)<br />
”←” falls l1 ⊆ l2 ⊆ . . . ⊆ ln ⊆ sch(R)<br />
(k) Eine Selektion kann mit einer Projektion vertauscht werden, falls die Projektion<br />
keine Attribute der Selektionsbedingung entfernt. Es gilt also folgendes:<br />
πl(σp(R)) = σp(πl(R))<br />
”→” falls attr(p) ⊆ l<br />
Ist dies nicht der Fall, kann die Vertauschung vorgenommen werden, wenn<br />
die Projektion um die notwendigen Attribute erweitert wird, die danach<br />
wieder wegprojiziert werden können:<br />
πl1(σp(πl2(R))) = πl1(σp(R))
12 KAPITEL 2. ALGEBRAISCHE OPTIMIERUNG<br />
”←” falls l1 ∪ attr(p) = l2 ⊆ sch(R)<br />
In der Praxis wird jedoch die Richtung von ”→” für Ersetzungen genutzt,<br />
so daß kein Aufblähen durch zusätzliche Projektionen erfolgt.<br />
(l) Jede Selektion kann an einer Joinoperation (oder einem Kreuzprodukt)<br />
” vorbeigeschoben“ werden, falls sie nur Attribute eines der beiden Join-<br />
Argumente verwendet. Enthält die Bedingung p beispielsweise nur Attribute<br />
aus R1, dann gilt auch:<br />
Allgemeiner gesagt gilt:<br />
σp(R1 ✶ R2) = σp(R1) ✶ R2<br />
σp(R1 × R2) = σp(R1) × R2<br />
σp(R1 ✶q R2) = σp0(σp1(R1) ✶q σp2(R2))<br />
falls attr(pi) ⊆ sch(Ri) für i = 1, 2 <strong>und</strong> p = p0 ∧ p1 ∧ p2 gilt. Für das<br />
Kreuzprodukt gilt entsprechend<br />
σp(R1 × R2) = σp0(σp1(R1) × σp2(R2))<br />
(m) Auf ähnliche Weise können auch Projektionen verschoben werden. Hier muß<br />
allerdings beachtet werden, daß die Joinattribute bis zum Join erhalten<br />
bleiben müssen:<br />
πl(R1 ✶p R2) = πl(πl1(R1) ✶p πl2(R2)) mit<br />
l1 = sch(R1) ∩ (l ∪ attr(p)) <strong>und</strong><br />
l2 = sch(R2) ∩ (l ∪ attr(p))<br />
(n) Desweiteren können Selektionen mit Mengenoperationen wie Vereinigung,<br />
Schnitt <strong>und</strong> Differenz vertauscht werden, also:<br />
σp(R1 ∪ R2) = σp(R1) ∪ σp(R2)<br />
σp(R1 ∩ R2) = σp(R1) ∩ σp(R2)<br />
σp(R1 − R2) = σp(R1) − σp(R2)<br />
Vorausgesetzt, daß die Definition des Durschnittes sch(R1) = sch(R2) beinhaltet,<br />
dann gilt zusätzlich dazu noch:<br />
σp(R1 ∩ R2) = σp(R1) ∩ R2<br />
Darüberhinaus reicht es bei der Differenz aus, die Tupel aus der ersten<br />
Relation herauszustreichen, so daß folgt:<br />
σp(R1 − R2) = σp(R1) − R2<br />
(o) Jeder Projektionsoperator kann mit der Vereinigung vertauscht werden. Sei<br />
sch(R1) = sch(R2), dann gilt:
2.1. RECHENREGELN DER RELATIONALEN ALGEBRA 13<br />
πl(R1 ∪ R2) = πl(R1) ∪ πl(R2)<br />
Eine Vertauschung der Projektion mit Durchschnitt <strong>und</strong> Differenz ist allerdings<br />
im allgemeinen falsch.<br />
(p) Eine Selektion <strong>und</strong> ein Kreuzprodukt können zu einem Join zusammengefaßt<br />
werden, wenn die Selektionsbedingung eine Joinbedingung ist, sie also<br />
Attribute einer Relation mit Attributen der anderen vergleicht:<br />
σ(R1.A1=R2.A2)(R1 × R2) = R1 ✶(R1.A1=R2.A2) R2<br />
Allgemeiner gesagt gilt für li ⊆ sch(Ri) mit i = 1, 2, <strong>und</strong> einem Vergleichsprädikat<br />
θ:<br />
σ(l1 θ l2)(R1 × R2) = R1 ✶(l1 θ l2) R2<br />
(q) Desweiteren können an Bedingungen Veränderungen vorgenommen werden.<br />
Da die Bedingungen aus Attributen, Konstanten, Prädikaten wie = <strong>und</strong> den<br />
logischen Verknüpfungen ∧, ∨ <strong>und</strong> ¬ bestehen, können alle hierfür geltenden<br />
Regeln benutzt werden. Beispielsweise kann eine Disjunktion mit Hilfe von<br />
DeMorgans Gesetz in eine Konjunktion umgewandelt werden, um vielleicht<br />
später die Anwendung von Regel (i) zu ermöglichen:<br />
¬(p1 ∨ p2) = ¬p1 ∧ ¬p2<br />
¬(p1 ∧ p2) = ¬p1 ∨ ¬p2<br />
Weiterhin ist diese Regel anwendbar, um Negationen ” von außen nach innen“<br />
zu schieben.<br />
(r) Es gilt wegen des Absorptions-Gesetzes für den natürlichen Verb<strong>und</strong>:<br />
R1 ✶ R2 = R1, falls R1 ⊆ R2<br />
(s) Weiter wird aus dem Anfragebaum ein dag“ (directed acyclic graph) bzw.<br />
”<br />
” collapsed tree“, wenn an zwei Knoten des Anfragebaumes das gleiche Zwischenergebnis<br />
vorliegt. Der Teilbaum τ muß nur einmal berechnet werden.<br />
✪❡❡<br />
′<br />
✪ τ<br />
�<br />
� ❅<br />
✪❡<br />
✪<br />
✪ τ ❡ τ<br />
❡<br />
✪ ❡<br />
✲<br />
✪❡❡<br />
′<br />
✪ τ<br />
❅<br />
✪<br />
✪❡<br />
✪ ❡<br />
✪<br />
τ<br />
Abbildung 2.1: Vom Anfragebaum zum ” dag“-Tree<br />
Vorsicht: Da aber dabei die Ergebnisrelation des mehrfach vorkommenden<br />
Teilbaumes zwischengespeichert werden muß, kommt es zu einem höheren<br />
Speicherplatzbedarf (Trade-Off). Es ist also abzuwägen, ob verringerter<br />
Zeit- oder verringerter Platzbedarf vorzuziehen ist.
14 KAPITEL 2. ALGEBRAISCHE OPTIMIERUNG<br />
Eine derartige Situation kann auch durch Anwendung der Rechengesetze<br />
hergestellt werden.<br />
(t) Außerdem kann das Zwischenergebnis an einem Knoten auch leer sein z. B.<br />
σp∧¬p(R) = σfalse(R) = ∅.<br />
In diesem Fall kann der Teilbaum, der von diesem Knoten ausgeht, gelöscht<br />
werden.<br />
✪❡<br />
✪ ❡<br />
�<br />
�<br />
✪❡<br />
✪ τ1❡<br />
❅<br />
✪❡<br />
✪ τ2❡<br />
τ ✲<br />
✪❡<br />
✪ ❡ τ<br />
❅<br />
✪❡<br />
✪ ❡ τ2<br />
Abbildung 2.2: Eliminierung leerer Teilbäume<br />
(u) Weiterhin ist eine Selektion auf ganz R red<strong>und</strong>ant:<br />
σtrue(R) = R<br />
(v) Schließlich kann ein entarteter Anfragebaum durch Anwendung der Regeln<br />
(f) <strong>und</strong> (g) zu einem ausgewogenen Operatorbaum umgewandelt werden.<br />
✁ ❆ ✶ ✁✁<br />
R1 R2<br />
✁<br />
✶ R4<br />
❆<br />
R3<br />
✁ ❆ ✶<br />
✲<br />
✁<br />
R1 R2<br />
✁ ❆ ✶ ✟✟✟ ✶ ❍<br />
❍❍<br />
✶<br />
✁<br />
R3 R4<br />
✁ ❆ Abbildung 2.3: Vom entarteten zu einem ausgewogenen Anfragebaum<br />
2.2 Beispiel für die algebraische Optimierung<br />
Zur besseren Verdeutlichung der bislang vorgestellten Ersetzungsregeln soll hier<br />
eine Anfrage in SQL algebraisch optimiert werden.<br />
Gegeben seien folgende Relationen R1 <strong>und</strong> R2:
2.2. BEISPIEL FÜR DIE ALGEBRAISCHE OPTIMIERUNG 15<br />
R1 A B C<br />
a 1 10<br />
b 1 20<br />
c 2 10<br />
d 2 35<br />
e 3 45<br />
Die zu bearbeitende Anfrage soll lauten:<br />
Select B, D<br />
From R1, R2<br />
Where R1.A= c ∧ R2.E= 2 ∧ R1.C= R2.C<br />
Mit folgendem Ergebnis:<br />
• 1:1-übersetzt:<br />
B D<br />
2 x<br />
πB,D(σ(R1.A=c∧R2.E=2∧R1.C=R2.C)(R1 × R2))<br />
πB,D<br />
×<br />
✓ ❙<br />
✓ ❙<br />
R1<br />
R2 C D E<br />
10 x 2<br />
20 y 2<br />
30 z 2<br />
40 x 1<br />
50 y 3<br />
σ (R1.A=c∧R2.E=2∧R1.C=R2.C)<br />
R2<br />
Abbildung 2.4: 1:1-übersetzter Anfragebaum<br />
Anfangs wird das Kartesische Produkt von R1 <strong>und</strong> R2 gebildet, um danach<br />
die benötigten Tupel selektieren zu können. Schließlich wird noch die<br />
verlangte Projektion durchgeführt.<br />
Insgesamt werden (1) + (25) + (5 + 5 · 5) = 56 Tupel bearbeitet. Genauer<br />
gesagt wird für das kartesische Produkt jedes Tupel der äußeren Relation<br />
R1 einmal gelesen, um dann mit jedem Innertupel aus R2 verb<strong>und</strong>en zu<br />
werden. Das bedeutet, daß die 5 Tupel in R1 je einmal gelesen werden <strong>und</strong><br />
jedes der 5 Tupel in R2 so oft gelesen wird, wie es Outertupel gibt (also<br />
5 mal). Das macht 5 + 5 · 5 Tupelzugriffe für das Produkt. Es entsteht<br />
eine Zwischenrelation mit 25 Tupeln. Bei der Selektion wird jedes dieser<br />
Tupel einmal bearbeitet, weitere 25 Zugriffe. Das nächste Zwischenergebnis<br />
enthält nur noch ein Tupel, auf dieses wird die Projektion angewandt, so<br />
daß noch ein Tupelzugriff hinzukommt.
16 KAPITEL 2. ALGEBRAISCHE OPTIMIERUNG<br />
• Algebraisch optimiert ergibt sich nach folgenden Transformationen<br />
der Anfragebaum:<br />
πB,D(σ(R1.A=c∧R2.E=2∧R1.C=R2.C)(R1 × R2))<br />
(i)<br />
= πB,D(σ(R1.A=c∧R2.E=2)(σ(R1.C=R2.C)(R1 × R2)))<br />
(p)<br />
= πB,D(σ(R1.A=c∧R2.E=2)(R1 ✶ R2))<br />
(l)<br />
= πB,D(σR1.A=c(R1) ✶ σR2.E=2(R2))<br />
πB,D<br />
✶<br />
✪ ❡<br />
σ<br />
✪ ❡<br />
(R1.A=c) σ (R2.E=2)<br />
R1<br />
Abbildung 2.5: Algebraisch-optimierter Anfragebaum<br />
R2<br />
Hierbei verschiebt man die Selektionen zu den Blättern nach unten <strong>und</strong><br />
ersetzt das Kartesische Produkt durch einen Join.<br />
In diesem Fall werden (1)+(1+1·3)+(5+5) = 15 Tupel bearbeitet. Zunächst<br />
wird auf die beiden Relationen R1, R2 je eine Selektion angewandt. Dafür<br />
muß in jeder Relation jedes Tupel einmal eingelesen werden, das ergibt je<br />
5 also 10 = 5 + 5 Zugriffe. Das Zwischenergebnis von σ(R1) enthält nur<br />
ein Tupel, das von σ(R2) hingegen 3. Wenn man als äußere Relation des<br />
Joins σ(R1) wählt (da diese Relation kleiner ist als σ(R2)), greift man auf<br />
jedes Tupel in σ(R1) bzw. σ(R2) nur einmal zu, das ergibt 4 = 1 + 1 · 3<br />
Zugriffe. Dazu kommt noch einer für die Projektion des einzigen Tupels in<br />
dem entstandenen Zwischenergebnis.<br />
Alleine durch die algebraische Transformation des Anfragebaumes konnte also die<br />
Anzahl der Tupelzugriffe um ca. 73% reduziert werden. Bei dieser Berechnung<br />
wird auch ersichtlich, daß die Größe der Zwischenergebnisse eine wichtige Angabe<br />
zur Optimierung darstellt. Würde man beim Join σ(R2) als äußere Relation<br />
wählen, käme man auf 3 + 3 · 1 = 6 Zugriffe, da das eine Tupel von σ(R1) für<br />
jedes der 3 Tupel in σ(R2) einmal gelesen werden müßte.<br />
Der Unterschied ist bei einem so einfachen Beispiel natürlich nicht besonders<br />
groß, da die Relationen in einer realen Datenbank aber im allgemeinen sehr viele<br />
Tupel enthalten, ist dieser Effekt auf jeden Fall zu beachten.
2.3. HEURISTISCHE ALGEBRAISCHE OPTIMIERUNG 17<br />
2.3 Heuristische algebraische Optimierung<br />
Mit Hilfe der im Abschnitt 2.1 erwähnten Ersetzungsregeln können nun heuristische<br />
Faustregeln formuliert werden. Sie basieren auf langjährigen Erfahrungen<br />
mit Optimierungen <strong>und</strong> sind bei fast allen Anfragen sinnvoll.<br />
Eine wichtige Faustregel ist, alle Zwischenergebnisse möglichst klein zu halten, da<br />
- wie schon erwähnt <strong>und</strong> im Kapitel 4 genauer erklärt - die Größe der Zwischenergebnisse<br />
einen sehr großen Einfluß auf die Bearbeitungszeit hat. Hierbei wird<br />
z. B. ausgenutzt, daß Selektionen <strong>und</strong> Projektionen im allgemeinen Zwischenergebnisse<br />
verkleinern.<br />
Dabei verringern Selektionen die Anzahl der Tupel, wohingegen Projektionen<br />
zunächst einmal die Länge des einzelnen Tupels verkürzen <strong>und</strong> im Falle einer<br />
Duplikateliminierung zusätzlich die Anzahl der Tupel reduziert wird.<br />
Faßt man jetzt alle Erfahrungen bezüglich der Anfrageoptimierung in Relationalen<br />
<strong>Datenbanken</strong> hinsichtlich einer algebraischen Optimierung zusammen, kann<br />
man folgende Standardregeln zur Erzeugung eines Prototypen als sinnvoll erachten<br />
(siehe dazu Vossen [57] <strong>und</strong> Mitschang [36]):<br />
• Unter Anwendung der Regel (g) des Abschnittes 2.1 zerlege man komplexe<br />
Verb<strong>und</strong>operationen in binäre Verb<strong>und</strong>e.<br />
• Unter Anwendung der Regeln (q) <strong>und</strong> (i) zerlege man Selektions-Operationen<br />
in eine Folge von Selektions-Operationen, deren Bedingung eine Konjunktion<br />
von elementaren Bedingungen ist.<br />
• Unter Anwendung der Regeln (h), (k), (l) <strong>und</strong> (n) vertausche man Selektionen<br />
mit anderen Operationen, wobei die Selektion soweit wie möglich im<br />
Ausdruck nach innen bzw. im Anfragebaum nach unten verschoben werden<br />
sollte.<br />
• Unter Anwendung der Regel (p) fasse man Produkte <strong>und</strong> Selektionen zu<br />
Joins zusammen.<br />
• Unter Anwendung der Regeln (j), (k), (m) <strong>und</strong> (o) zerlege man Projektionen<br />
<strong>und</strong> verschiebe sie im Ausdruck so weit wie möglich nach innen; dabei<br />
erzeuge man gegebenenfalls zusätzliche Projektions-Operationen.<br />
• Unter Anwendung der Regeln (f), (g) <strong>und</strong> (v) rearrangiere man die Blätter<br />
des Anfragebaums so, daß diejenigen Operanden, welche mit den restriktivsten<br />
Selektions-Operationen versehen sind, am weitesten links stehen (unter<br />
der Annahme, daß ein solcher Baum in LRW-Ordnung (Links-Rechts-<br />
Wurzel-Durchlauf / Postorder) ausgewertet wird).
18 KAPITEL 2. ALGEBRAISCHE OPTIMIERUNG<br />
• Zum Schluß sollten direkt aufeinander folgende Selektionen bzw. Projektionen<br />
mittels (i) <strong>und</strong> (j) zusammengefaßt <strong>und</strong> leere Teilbäume eliminiert<br />
werden (t) <strong>und</strong> (e).<br />
2.4 Tableau-Optimierung<br />
Ein weiteres Verfahren um einen algebraischen Prototypen zu bekommen, ist die<br />
Tableauoptimierung. Dabei existieren, für red<strong>und</strong>ante Verb<strong>und</strong>-Operationen aus<br />
einem gegebenen relationenalgebraischen Ausdruck, formale Tableau-Methoden.<br />
Diese bieten nachgewiesenerweise die beste algebraische Optimierung eines Selektion-Projektion-Join-Ausdruckes<br />
(SPJ-Ausdruck).<br />
Dafür wird ein gegebener SPJ-Ausdruck zunächst in ein spezielles Tableau übersetzt,<br />
welches eine Zeile mehr hat, als in der Anfrage Verb<strong>und</strong>-Operationen vorkommen.<br />
Dieses Tableau wird dann mit dem Ziel der Elimination von Zeilen (<strong>und</strong><br />
damit red<strong>und</strong>anter Verb<strong>und</strong>-Operationen) optimiert <strong>und</strong> schließlich in einen optimierten,<br />
äquivalenten SPJ-Ausdruck rückübersetzt.<br />
Für eine nähere Erläuterung bezüglich - der hier erwähnten Tableau-Optimierung<br />
- verweise ich auf die Dissertation von Yehoshua Sagiv [47].<br />
2.5 Fazit der algebraischen Optimierung<br />
Bei den meisten Regeln ist nur eine Richtung für die Optimierung eines Anfragebaums<br />
wichtig, da nur diese eine Verbesserung der Anfrage ermöglicht (siehe<br />
dazu Kapitel 6). Zusätzlich dazu ergaben sich im Laufe der Zeit Erkenntnisse<br />
bezüglich der Ersetzungsregeln, die hier kurz zusammengefaßt werden sollen.<br />
Die im ersten Abschnitt vorgestellten Regeln (a) bis (g) <strong>und</strong> (r) werden später<br />
nicht mehr benötigt, da sie von der benutzten Datenstruktur selber übernommen<br />
werden, das heißt z. B. das Kommutativität <strong>und</strong> Assoziativität der Operatoren<br />
in der Implementierung verankert sind. Ihre Existenz ist aber maßgeblich für alle<br />
darauf folgenden Ersetzungsregeln.<br />
Bei der Regel (l) wird stillschweigend vorausgesetzt, daß der Optimierer die Selektionen<br />
zu den zugehörigen Relationen zuweisen kann, wobei er bei jedem Schritt<br />
genau die richtige Operation bearbeitet. Dafür wird aber eine spezielle Reihenfolge<br />
der Selektionen benötigt. Gr<strong>und</strong> dafür ist die rekursive Baumdurchwanderung,<br />
die eine leichte Ersetzung der einzelnen Operatoren zuläßt. Bei dieser müssen die<br />
benötigten Selektionen so aufgeteilt werden, daß sie flexibel im Baum verschoben
2.5. FAZIT DER ALGEBRAISCHEN OPTIMIERUNG 19<br />
werden können. Meistens hilft es, wenn man die restriktivste Selektion als letzte<br />
bearbeitet.<br />
Letztendlich wird die richtige Reihenfolge der Selektion durch einen eigens für diese<br />
Aufgabe programmierten Optimierer erledigt, der für jeden Fall die benötigte<br />
Reihenfolge liefert - unabhängig von der Eingabe der Konditionen. Eine einfache<br />
Lösung dieser Problematik besteht darin, die vorher vorgestellte Regel (i)<br />
zu benutzen, denn diese zerlegt die Selektionsbedingungen in atomare Prädikate,<br />
bei denen man die Reihenfolge vorgeben kann. Somit wird die Sortierung der<br />
Selektionen von der Regel (i) vorgenommen, bei der die spätere Implementierung<br />
einfacher ist.<br />
Die Regel (o) wird in den meisten Büchern über Anfrageoptimierung auch mit<br />
Durchschnitt <strong>und</strong> Differenz angegeben (z. B. bei Mitschang [36]). Dieses ist aber<br />
falsch. Zur Erklärung hier ein kurzes Gegenbeispiel:<br />
Seien folgende Relationen R1, R2 mit gleichen Schemata gegeben:<br />
R1 A B C<br />
2 2 3<br />
3 3 1<br />
R2 A B C<br />
1 1 1<br />
2 2 1<br />
Die gewünschte Operation sei πA,B(R1) ∩ πA,B(R2). Somit bekommt man als Ergebnisrelation:<br />
A B<br />
2 2<br />
Wenn man jetzt aber den Durchschnitt vor der Projektion ausführt πA,B(R1 ∩<br />
R2), bekommt man ein anderes Ergebnis; nämlich die leere Menge. Daraus folgt<br />
πA,B(R1) ∩ πA,B(R2) �= πA,B(R1 ∩ R2).<br />
Gleiches gilt auch für die Differenz zweier Relationen. Auch da können unterschiedliche<br />
Ergebnisse vorkommen.<br />
Die im vorigem Abschnitt vorgestellte Heuristik basiert auf dem Ablauf, der bei<br />
Vossen [57] wieder zu finden ist. Dabei wurde die Regel (p) hinzugefügt, da die<br />
meisten Bücher annehmen, daß Kartesische Produkte gar nicht mehr existieren,<br />
sondern generell schon - bei der 1:1-Übersetzung in den Anfragebaum - als Joins<br />
interpretiert werden. Dieser Sachverhalt entzieht aber dem Nutzer die Möglichkeit,<br />
bei der Bearbeitung der Heuristik einzelne Abläufe zu verändern <strong>und</strong> den<br />
speziellen Beispielen anzupassen (bei der später vorgestellten Implementierung<br />
werden dafür mehrere Beispiele vorgeführt).<br />
Weitere Bücher (darunter Mitschang [36] <strong>und</strong> Kemper [29]) haben teilweise andere<br />
Abläufe, die aber - nach mehreren Testdurchläufen mit verschiedenen Bei-
20 KAPITEL 2. ALGEBRAISCHE OPTIMIERUNG<br />
spielen - als weniger geeignet erschienen. Meistens wurde nur ein rudimentärer<br />
Algorithmus angegeben, bei dem das Aufbrechen der Selektionen <strong>und</strong> darauffolgende<br />
Verschiebungen in die Äste des Anfragebaumes die einzigen Optimierungen<br />
darstellte.<br />
Am Beispiel 2.2 ist leicht zu erkennen, daß nicht immer ein vollständiger Ablauf<br />
der Heuristik benötigt wird. In diesem Fall werden nur drei Regeln benutzt, um<br />
den gesamten Anfragebaum zu optimieren.
Kapitel 3<br />
Physische Optimierung<br />
Je weniger Erkenntnisse ein Mensch besitzt,<br />
desto ferner fühlt er sich zu Gott.<br />
Albert Einstein<br />
Bei der physischen Optimierung geht es darum, die logischen Operatoren mit<br />
Hilfe von physischen Operatoren möglichst kostengünstig zu realisieren. Dabei<br />
können für einen logischen Operator mehrere physische Operatoren existieren,<br />
die eventuell auch zusätzliche Informationen (z. B. Indexe) benötigen. Außerdem<br />
sind für die physische Optimierung noch zusätzliche Operatoren, die kein logisches<br />
Äquivalent haben, zu berücksichtigen (z. B. Sortierung oder Indexerzeugung).<br />
Die physischen Operatoren lassen sich unter Zuhilfenahme von Iteratoren definieren.<br />
Dieses Konzept wird im ersten Abschnitt näher erläutert. Die weiteren<br />
Abschnitte beschäftigen sich mit den verschiedenen Zugriffsmethoden 1,2 :<br />
Zugriffsmethoden<br />
❄ ❄ ❄<br />
Scan-Methoden Mengen-Oper. Join-Methoden<br />
❄<br />
Sequential Scan<br />
❄<br />
Index Scan<br />
❄<br />
Produkt<br />
Differenz<br />
❄❄<br />
❄<br />
Vereinigung Hash Join<br />
Durchschnitt<br />
Semijoin 1<br />
❄ ❄<br />
❄ ❄<br />
❄ ❄<br />
Merge Join Index Join<br />
Nested Loop<br />
Antisemijoin 2<br />
Abbildung 3.1: Zugriffsmethoden der physischen Optimierung<br />
Eine wichtige Zusatzinformation für die physische Optimierung können z. B. die<br />
Integritätsbedingungen sein. Deswegen werden dafür zusätzliche Informationen<br />
1 Semijoin ist kein eigenständiger Join, sondern wird aus den anderen Join-Varianten erzeugt.<br />
2 Hierbei handelt es sich, um das Komplement zum Semijoin.<br />
21
22 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
aus statischen Datenbeständen oder statistischen Auswertungen zu Hilfe genommen.<br />
Gr<strong>und</strong>lage sind dabei Auswertungen bisheriger Anfragen, deren Ausführungspläne<br />
sowie die dadurch entstandenen Kosten. Problematisch bleibt - wie<br />
wir später sehen werden - die Beschaffung des zusätzlichen Wissens über das<br />
Datenbanksystem.<br />
3.1 Iteratoren <strong>und</strong> Iteratorbäume<br />
Zwischenergebnisse bei der Auswertung von Anfragebäumen müssen in der Regel<br />
nicht gespeichert werden. Sie könnten aber in vielen Fällen so groß werden, daß<br />
sie nicht komplett in den Hauptspeicher passen. Würde man sie vollständig berechnen,<br />
bevor man den nächsten Knoten im Anfragebaum abarbeitet, so hätte<br />
man zusätzlichen Aufwand zum Laden bzw. Verdrängen dieser Zwischenergebnisse.<br />
Um diese Kosten zu vermeiden, implementiert man die Operationen eines<br />
Anfragebaumes mit sogenannten Iteratoren.<br />
Ein Iterator liefert keine kompletten Zwischenergebnisse, sondern liefert nur die<br />
Ergebnistupel einzeln weiter zu dem ihm übergeordneten Iterator.<br />
Deshalb bietet jeder Iterator folgende Funktionen:<br />
• open: öffne den Iterator (beginne die Suche nach Elementen beim 1. Element)<br />
• next: gebe das nächste Tupel der Berechnung weiter<br />
• close: schließe den Iterator<br />
• size: schätze die Größe des Ergebnisses ab<br />
• cost: schätze die Kosten der Berechnung ab<br />
Physische Optimierung übersetzt deshalb logische Anfragebäume der relationalen<br />
Algebra in sogenannte Iteratorbäume, wobei jeder Iterator für einen Algebraoperator<br />
im Algebra-Anfragebaum steht.<br />
Dabei öffnet das Anwendungsprogramm einen Iterator <strong>und</strong> fordert mit next solange<br />
Tupel an, bis keine mehr geliefert werden. Danach wird der Iterator geschlossen<br />
(end-of-iter Funktion). Für die Berechnung der Ergebnistupel benötigt<br />
dieser Iterator die Ausgaben der mit ihm verb<strong>und</strong>enen Tochteriteratoren. Deshalb<br />
wird er bei den Tochteriteratoren wieder open, next <strong>und</strong> close aufrufen.<br />
Dies wird bis zu den Blättern des Baumes, an denen sich die Basisrelationen der<br />
Datenbank befinden, fortgesetzt.<br />
Zusätzlich zu einer eleganteren Architektur bietet das Iteratorkonzept den Vorteil,<br />
daß man nicht notwendigerweise Zwischenergebnisse speichern muß. Nehmen<br />
wir mal an, daß eine Anfrage nur Projektionen <strong>und</strong> Selektionen beinhalten würde,
3.1. ITERATOREN UND ITERATORBÄUME 23<br />
Anwendungsprogramm<br />
Iterator<br />
open next close size cost<br />
Iterator<br />
�<br />
�<br />
❅<br />
❅<br />
Iterator<br />
open next close size cost open next close size cost<br />
�<br />
� ❅ ✎☞ ❅✎☞<br />
R3<br />
✍✌<br />
R4<br />
✍✌<br />
Iterator<br />
open next close size cost<br />
�<br />
� ❅ ✎☞ ❅✎☞<br />
R3<br />
✍✌<br />
✍✌<br />
Abbildung 3.2: Schematische Darstellung eines Auswertungsplanes<br />
dann hätten wir bei Prozeduren, die jeweils einen algebraischen Operator komplett<br />
berechnen, im allgemeinen für jedes Teilergebnis eine Speicherung der Zwischenergebnisse.<br />
Bei der schrittweisen Realisierung werden die Ergebnisse Stück<br />
für Stück durchgereicht, was man dann als kostensparendes Pipelining verstehen<br />
kann.<br />
Zur Vereinfachung der Darstellung der folgenden Iteratoren sollen Trivialoperationen,<br />
die bei jedem Iterator vorkommen, hier einmal erwähnt werden <strong>und</strong> später<br />
als Gr<strong>und</strong>lage zur Verfeinerung der jeweiligen Iteratoren dienen (z. B. muß bei<br />
manchen Iteratoren open, next das nächste next vorbereiten):<br />
iterator Generisch<br />
open<br />
• Initialisierung (Öffnen des Eingabestroms (ES) <strong>und</strong> des Ausgabestroms (AS))<br />
next<br />
• Berechnung des nächsten Ergebniselements (<strong>und</strong> Bereitstellung desselben im<br />
AS)<br />
close<br />
• Abschließen der Verarbeitung (Schließen der offenen Objektströme <strong>und</strong> Freigeben<br />
temporärer Datenbereiche)<br />
R4<br />
Abbildung 3.3: Generischer Iterator
24 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
3.2 Scan-Methoden<br />
Unter den Scan-Methoden werden diejenigen physischen Operatoren zusammengefaßt,<br />
die nur auf einer Relation arbeiten, d.h. die Entsprechungen zu Projektion<br />
<strong>und</strong> Selektion.<br />
3.2.1 Projektion<br />
Da die Projektion in der physischen Algebra ausschließlich die Projektion der<br />
Tupel <strong>und</strong> nicht die Duplikateliminierung durchführen soll, sieht der Iterator zur<br />
Projektion wie folgt aus:<br />
iterator π Rel<br />
l<br />
open<br />
next<br />
• Fordere Tupel aus der Eingabe an<br />
• Projiziere das Tupel auf die Attributmenge l<br />
• Gib dieses Tupel aus<br />
close<br />
Abbildung 3.4: Projektions-Iterator<br />
Die Komplexität beläuft sich hierbei auf O(n).<br />
3.2.2 Selektion<br />
Die hier vorgestellte Implementierung kann man als Brute-Force Variante ansehen.<br />
Bei jedem Aufruf von next wird ein die Bedingung erfüllendes Tupel<br />
zurückgeliefert. Die Komplexität erfüllt dabei eine Abschätzung von O(n) (siehe<br />
Abbildung 3.5).<br />
Bei der nachfolgenden Variante wird der Zugriff über eine Indexstruktur ermöglicht<br />
(sei es eine B-Baum- oder eine Hashtabellen-Implementierung). Hierbei wird<br />
beim Öffnen des Iterators schon das erste passende Tupel nachgeschlagen. Bei<br />
einem B + -Baum erreicht man das, indem man innerhalb des Baumes bis zu den<br />
Blättern absteigt. Die Blätter können dann bei jedem next-Aufruf sequentiell<br />
durchsucht werden, bis die Bedingung p nicht mehr zutrifft (siehe Abbildung<br />
3.6).
3.3. JOIN-METHODEN (✶P , ⊲⊳P ) 25<br />
iterator σ Rel<br />
p<br />
open<br />
next<br />
• Hole solange nächstes Tupel der Eingabe, bis eines die Bedingung p erfüllt<br />
• Gib dieses Tupel aus<br />
close<br />
iterator σ Ind<br />
p<br />
open<br />
next<br />
Abbildung 3.5: Selektions-Iterator<br />
• Schlage im Index die TID des nächsten Tupels nach, welches die Bedingung p<br />
erfüllt<br />
• Lies das Tupel zur TID <strong>und</strong> gib es aus<br />
close<br />
Abbildung 3.6: Selektions-Iterator mit Zugriff über vorhandenen Index<br />
3.3 Join-Methoden (✶p, ⊲⊳p)<br />
Der relationale Join-Operator (✶p) verknüpft zwei Relationen miteinander <strong>und</strong><br />
erzeugt dadurch die Ausgaberelation. Meistens werden Joins zur Vermeidung von<br />
kartesischen Produkten eingesetzt. Die Verknüpfung von Tupeln der Eingaberelationen<br />
erfolgt genau dann, wenn das Verb<strong>und</strong>prädikat von den Partnertupeln<br />
erfüllt wird.<br />
Verb<strong>und</strong>operationen werden durch drei Merkmale charakterisiert (siehe dazu Mitschang<br />
[36]):<br />
• Verb<strong>und</strong>prädikat:<br />
Je nachdem, welches Prädikat verwendet wird, unterscheidet mandenGleichheitsverb<strong>und</strong><br />
(Gleichheitsbedingung als Prädikat), den Ungleichheitsverb<strong>und</strong><br />
(Ungleichheitsverb<strong>und</strong> als Prädikat) <strong>und</strong> den Intervallverb<strong>und</strong> (echte<br />
Komparatoren als Prädikat, d.h. wenn der Verb<strong>und</strong> keine dieser Bedingungen<br />
erfüllt, kann nur der nachfolgende NestedLoop-Join benutzt werden).<br />
• Anzahl der Verb<strong>und</strong>partner:<br />
Zwei Verb<strong>und</strong>partner erzeugen einen binären Verb<strong>und</strong>, ansonsten spricht<br />
man von einem Mehrwegverb<strong>und</strong> bzw. n-nären Verb<strong>und</strong>, der aber mittels
26 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
Assoziativgesetz in mehrere binäre Joins aufgespalten wird.<br />
• Art des Verb<strong>und</strong>es:<br />
Die in dem Verb<strong>und</strong> realisierten Verknüpfungen der Partnerrelationen können<br />
für den Fall des Binärverb<strong>und</strong>es entweder 1:1, 1:n oder m:n sein.<br />
Desweiteren stellt der dazugehörige Antijoin (⊲⊳p) die Negierung oder besser gesagt,<br />
die Komplementäroperation zum Join dar. Werden beim Join Elemente<br />
kombiniert, falls das Joinprädikat von beiden erfüllt ist, so werden beim Antijoin<br />
gerade die Elemente verb<strong>und</strong>en, die das Prädikat nicht erfüllen.<br />
In den folgenden Abschnitten werden vier Implementierungsvarianten des Join-<br />
Operators in Bezug auf Binärverb<strong>und</strong>e beschrieben <strong>und</strong> erklärt. Hierbei wird die<br />
Iteratorschreibweise zur Beschreibung der Varianten benutzt. Im Anschluß daran<br />
wird noch auf die Verwandtschaft des Join-Operators zu den Mengenoperatoren<br />
hingewiesen.<br />
3.3.1 NestedLoop-Join (✶NestedLoop p )<br />
Die wahrscheinlich erste Idee, die man zur Implementierung einer Verb<strong>und</strong>operation<br />
hat, ist die Schleifenschachtelung (engl. Nested Loop). Hierbei wird jedes Tupel<br />
des ersten Operanden mit jedem Tupel des zweiten verglichen. Bei Erfüllung<br />
der Join-Bedingung werden die beiden Tupel miteinander verb<strong>und</strong>en.<br />
Die Schleifeniteration läßt sich wie folgt mit Iteratoren darstellen:<br />
iterator ✶ NestedLoop<br />
p<br />
open<br />
• öffne rechten Iterator, rufe solange next auf bis der Iterator keine Tupel mehr<br />
liefert <strong>und</strong> speichere jedes so erhaltene Tupel in einer temporären Relation temp<br />
• hole erstes linkes Tupel <strong>und</strong> setze Zeiger auf das erste Tupel von temp<br />
next<br />
• setze den Zeiger auf temp solange weiter bis Bedingung p erfüllt ist<br />
– falls dabei temp keine Tupel mehr liefert, so hole nächstes linkes Tupel<br />
<strong>und</strong> setze Zeiger auf erstes Tupel von temp<br />
• gib Join der beiden Tupel aus<br />
close<br />
Abbildung 3.7: NestedLoop-Iterator
3.3. JOIN-METHODEN (✶P , ⊲⊳P ) 27<br />
Es ist offensichtlich, daß die Komplexität des gerade beschriebenen Verb<strong>und</strong>es<br />
(Laufzeit bei gleichen Eingabelängen O(n 2 )) vom Aufwand des Zugriffes auf die<br />
Eingabeströme abhängt. Deshalb gibt es eine Vielzahl von Verbesserungsmöglichkeiten.<br />
Die erste mögliche Zugriffsoptimierung liegt darin, statt der Durchführung der<br />
Schleifeniteration auf Elementbasis ganze Cluster als Schleifengranulat zu verwenden,<br />
die je nach Blockgröße aus einer bestimmten Elementanzahl bestehen.<br />
Aufgr<strong>und</strong> der Tatsache, daß mit Hilfe dieser Verfahrensänderung die Zahl der<br />
Schleifeniterationen drastisch minimiert werden kann, reduziert sich die Komplexität<br />
erheblich.<br />
Darüberhinaus ist die Schleifeniteration auch kombinierbar mit Indexzugriffen,<br />
wobei es unwichtig ist, welche Zugriffsstruktur verwendet wird (Index- oder Hash-<br />
Strukturen).<br />
3.3.2 Index-Join (✶Index p )<br />
Eine durch die Nutzung eines Index verbesserte Verb<strong>und</strong>operation wird Index-<br />
Join genannt. Dabei wird ein Index auf dem Joinattribut einer oder beider Relationen<br />
als vereinfachte Zugriffsmethode benutzt. Manchmal lohnt sich sogar die<br />
Schaffung eines Indexes, um den Zugriff zu beschleunigen. Es sollte auch erwähnt<br />
werden, daß die Iteratoren TID-Folgen liefern <strong>und</strong> nicht wie die anderen Implementierungen<br />
Tupel wieder zurückgeben. Die Index-Join Varianten stellen sich<br />
mit Iteratoren wie folgt dar (siehe Tabelle 3.8).<br />
iterator ✶ Rel,Index<br />
p<br />
open<br />
next<br />
• Falls TID-Folge nicht vorhanden, hole nächstes Tupel aus der linken Eingabe,<br />
schlage Joinattributwert des linken Tupels im Index nach, setze Zeiger auf den<br />
ersten Eintrag der dadurch erhaltenen TID-Folge<br />
• Falls TID-Folge vorhanden, setze Zeiger auf den nächsten linken TID<br />
• Schlage passendes Tupel zum aktuellen TID nach<br />
• Bilde Join <strong>und</strong> gib das Jointupel aus<br />
close<br />
Abbildung 3.8: Relationen-Index-Join-Iterator
28 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
iterator ✶ Index,Index<br />
p<br />
open<br />
next<br />
• Falls TID-Folge nicht vorhanden, hole nächstes TID aus dem Index der linken<br />
Eingabe, hole passendes Tupel, schlage Joinattributwert des linken Tupels im<br />
Index der rechten Eingabe nach, setze Zeiger auf den ersten Eintrag der dadurch<br />
erhaltenen TID-Folge<br />
• Falls TID-Folge vorhanden, setze Zeiger auf den nächsten TID<br />
• Schlage passendes Tupel zum aktuellen TID der TID-Folge nach<br />
• Bilde Join <strong>und</strong> gib das Jointupel aus<br />
close<br />
Abbildung 3.9: Index-Index-Join-Iterator<br />
Dabei wird beim Index-Index-Join davon ausgegangen, daß wir einen Äqui-Join<br />
vorliegen haben. Der Iterator agiert vergleichbar zu einem Merge-Join, im Unterschied<br />
dazu wird aber über einen Index auf die Tupel zugegriffen.<br />
Der Relationen-Index-Join hat eine Komplexität von O(n·log(n)) <strong>und</strong> der Index-<br />
Index-Join besitzt eine Laufzeit von O(n), falls beide Eingabeströme einen Index<br />
besitzen.<br />
3.3.3 Merge-Join (✶Merge p )<br />
Bei der Implementierung lassen sich die Kosten der Realisierung einer Verb<strong>und</strong>operation<br />
reduzieren, indem die jeweiligen Operanden bereits sortiert im Speichersystem<br />
vorliegen bzw. vor dem eigentlichen Verb<strong>und</strong> in eine temporäre Relation<br />
sortiert werden. Diese Zugriffsmethode wird dann als Misch-Verb<strong>und</strong> (engl.<br />
Merge-Join) bezeichnet. Dabei müssen die Operanden sortiert (bezüglich des<br />
Joinattributes) vorliegen, um danach jeweils sequentiell durchsucht zu werden.<br />
Hierbei werden die zu verbindenen Tupel, die die Join-Bedingung erfüllen, jeweils<br />
einzeln dem Resultat zugewiesen.<br />
Bedingung für den Merge-Join ist, daß die linke Eingabe keine Duplikate bezüglich<br />
des Joinattributes enthält. Sollte dies doch der Fall sein, kann nach der Anwendung<br />
des Kommutativgesetzes für Joins trotzdem ein Merge-Join verwendet werden.<br />
Merge-Join besitzt eine Laufzeit von O(n), falls die beiden Eingangsströme schon<br />
sortiert vorhanden sind.
3.3. JOIN-METHODEN (✶P , ⊲⊳P ) 29<br />
Der Merge-Join (für unique-Join-Attribute) läßt sich wie folgt mit Iteratoren<br />
darstellen:<br />
iterator ✶ Merge<br />
p<br />
open<br />
• Öffne beide Eingaben<br />
• Hole linkes <strong>und</strong> rechtes Tupel<br />
next<br />
• Solange bis p erfüllt ist<br />
– bestimme Eingabe mit kleinerem anliegendem Joinattributwert<br />
– hole aus dieser Eingabe das nächste Tupel<br />
• Gib den Join der beiden aktuellen Tupel aus.<br />
close<br />
3.3.4 Hash-Join (✶Hash p )<br />
Abbildung 3.10: Merge-Join-Iterator<br />
Der Hash-Join nutzt eine dynamisch aufgebaute Hash-Funktion zum schnellen<br />
Auffinden von Verb<strong>und</strong>partnern. Die Hash-Funktion wird dafür verwendet, beide<br />
Relationen in Partitionen zu zerlegen. Der Vorteil dabei ist, daß nicht mehr alle<br />
Tupel miteinander verglichen werden müssen, sondern nur noch Tupelpaare, die<br />
in Partitionen mit gleichem Hashwert vorkommen.<br />
Meistens ist die Tabelle so klein, daß sie im Speicher gehalten werden kann; somit<br />
genügt ein einmaliges Lesen des anderen Join-Operanden, um den Verb<strong>und</strong> zu<br />
berechnen.<br />
Die Komplexität des Hash-Joins wird mit O(k ·n) angegeben, wobei k für die Anzahl<br />
der Partitionen steht. Der Hash-Verb<strong>und</strong> ist im wesentlichen nur für Gleichheitsprädikate<br />
mit θ ∈ {=} geeignet, aber man kann ihn auch prinzipiell bei<br />
allen anderen einsetzen. Für eine genauere Darstellung dieses komplexen Themas<br />
verweise ich deshalb auf die Arbeit von H. Garcia-Molina, J. D. Ullman <strong>und</strong> J.<br />
Widom [37].
30 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
iterator ✶ Hash<br />
p<br />
open<br />
• Öffne kleinere Relation (Build Input)<br />
• Partitioniere Build Input solange bis die Partitionen in den Hauptspeicher passen.<br />
• Öffne die andere Relation (Probe Input)<br />
• Partitioniere Probe Input mit der gleichen Hashfunktion<br />
• Lade erste Partition des Build Inputs in den Hauptspeicher <strong>und</strong> setze Zeiger<br />
auf das erste Tupel<br />
• Lade erste Partition des Probe Inputs in den Hauptspeicher<br />
next<br />
• Hole solange Tupel aus der aktuellen Partition des Probe Inputs bis die Bedingung<br />
p erfüllt ist.<br />
– Falls die Partition von Probe Input leer ist, setze den Zeiger auf das<br />
nächste Tupel der aktuellen Partition von Build Input<br />
– Falls die Partition von Build Input ebenfalls leer ist, lade jeweils die<br />
nächste Partition von Probe <strong>und</strong> Build Input <strong>und</strong> setze den Zeiger auf<br />
das erste Tupel der Partition des Build Inputs<br />
• Gebe das gef<strong>und</strong>ene Paar aus<br />
close<br />
Abbildung 3.11: Hash-Join-Iterator<br />
3.3.5 Semijoin (⋉p, ⋊p) - Antisemijoin ( ¯⋉p)<br />
Der Semijoin ⋉p ist für Relationen R1, R2 wie folgt definiert:<br />
R1 ⋉p R2 = R2 ⋊p R1<br />
= πsch(R1)(R1 ✶p R2)<br />
= R1 ✶p πattr(p)(R2)<br />
Das heißt, daß ein Semijoin aus der Relation R1 all diejenigen Tupel heraussucht,<br />
die bezüglich des Prädikates p mindestens einen Joinpartner in der Relation R2<br />
besitzen.<br />
Für einen Semijoin gibt es genau die gleichen Möglichkeiten wie für einen Join:<br />
Man kann ihn als NestedLoop-, Merge-, Index- <strong>und</strong> Hash-Semijoin implementieren.<br />
Somit erhält man die entsprechenden Laufzeiten wie bei den passenden
3.3. JOIN-METHODEN (✶P , ⊲⊳P ) 31<br />
Joins. Der Iterator zu den verschiedenen Semijoin-Operatoren ist jeweils fast der<br />
gleiche wie bei dem Join-Operator, mit dem kleinen Unterschied, daß stets statt<br />
des Verb<strong>und</strong>es zweier passender Tupel nur das linke Tupel ausgegeben wird <strong>und</strong><br />
die Suche nach Join-Partnern in der rechten Eingabe (Relation bzw. Index) abgebrochen<br />
wird, falls ein Partner für das linke Tupel gef<strong>und</strong>en wurde.<br />
Für Join <strong>und</strong> Semijoin gilt:<br />
R1 ✶p R2 = (R1 ⋉p πattr(p)(R2)) ✶p R2.<br />
Aus dieser Gleichung folgt die Möglichkeit, bei verteilten <strong>Datenbanken</strong> den Transfer<br />
zu verringern. Soll ein Join zweier Relationen vorgenommen werden, die auf<br />
zwei verschiedenen Sites gespeichert sind, kann zunächst die Projektionπattr(p)(R2)<br />
gebildet <strong>und</strong> versendet werden, daraufhin mittels eines Semijoins R1⋉pπattr(p)(R2)<br />
die ” überflüssigen“ Tupel (sogenannte dangling-tupel) der Relation R1 eliminiert<br />
<strong>und</strong> die benötigten Tupel, also das erhaltene Zwischenergebnis, zurückgesandt<br />
werden. Zum Abschluß wird ein Join dieses Zwischenergebnisses <strong>und</strong> der Relation<br />
R2 gebildet. Insbesondere wenn es viele dangling-Tupel gibt, spart diese<br />
Ausführung viel Datentransfer ein.<br />
Der Antisemijoin ( ¯⋉p) liefert im Gegensatz zum Semijoin alle die Tupel aus R1,<br />
die keinen Joinpartner in der zweiten Relation R2 haben:<br />
R1 ¯⋉p R2 = R1 − (R1 ⋉p R2)<br />
3.3.6 Outer-Joins (❂⋊p, ⋉❁p, ❂×❁p)<br />
Die bislang eingeführten Join-Operator nennt man normalerweise Inner-Joins, da<br />
bei dieser Variante die Tupel der Argumentrelation verloren gehen, die keinen<br />
Joinpartner gef<strong>und</strong>en haben. Bei der Outer-Join Operation werden je nach Typ<br />
auch partnerlose Tupel der linken, der rechten bzw. beider Argumentrelationen<br />
ins Ergebnis übernommen, dabei werden die fehlenden Attribute bei den partnerlosen<br />
Tupeln mit Nullwerten aufgefüllt.<br />
• Left-Outer-Join (❂⋊p)<br />
Die partnerlosen Tupel der linken Relation, rechts aufgefüllt mit Nullwerten,<br />
werden dem Ergebnis hinzugefügt.<br />
• Right-Outer-Join (⋉❁p)<br />
Die partnerlosen Tupel der rechten Relation, links aufgefüllt mit Nullwerten,<br />
werden dem Ergebnis hinzugefügt.
32 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
• Full-Outer-Join (❂×❁p)<br />
Die partnerlosen Tupel beider Relationen, rechts bzw. links aufgefüllt mit<br />
Nullwerten, werden dem Ergebnis hinzugefügt.<br />
Die verschiedenen Iteratoren der Left-, Right- <strong>und</strong> Full-Outer-Joins ähneln den<br />
vorher vorgestellten Alternativen NestedLoop-, Merge-, Index- oder Hash-Join,<br />
wobei nach Ausführung des Inner-Joins in der next-Phase je eines der restlichen<br />
verlangten Tupel ausgegeben wird.<br />
3.4 Mengenoperatoren<br />
Die in jeder Datenbank implementierten Mengenoperatoren wie Vereinigung, Differenz,<br />
Durchschnitt <strong>und</strong> Produkt lassen sich leicht auf Verb<strong>und</strong>bildung zurückführen.<br />
So ist zum Beispiel das kartesische Produkt ein Spezialfall des Join-Operators<br />
mit true als Verb<strong>und</strong>bedingung. Somit werden alle Tupel des einen Joinpartners<br />
mit allen Tupeln des anderen Verb<strong>und</strong>partners kombiniert.<br />
Insgesamt erzeugt das Produkt immer eine maximale Ergebnisgröße bezogen sowohl<br />
auf Tupelanzahl als auch auf Attributanzahl. Im Vergleich dazu berechnet<br />
eine Joinoperation, aufgr<strong>und</strong> der benutzten Selektion, weniger Ergebniselemente<br />
mit zum Teil weniger Attributen, deshalb sollte man möglichst jedes Produkt in<br />
kostengünstige Joins verwandeln.<br />
Interessanterweise ist die Hash-Methode für das Kartesische Produkt ungeeignet,<br />
da sie im wesentlichen auf einer geschickten <strong>und</strong> scharfen Partionierung beruht,<br />
die für diese Operation nicht existiert. Im Gegensatz dazu könen bei den anderen<br />
Mengenoperationen beide Eingabeströme, abhängig von der jeweiligen Operation,<br />
in der gleichen Hash-Tabelle organisiert werden, aus der dann das Ergebnis<br />
einfach abzuleiten ist.<br />
Bei allen anderen Mengenoperationen läßt sich der Operator durch Gleichheit der<br />
Attribute <strong>und</strong> geeignete Joinvarianten realisieren.<br />
Gegeben seien zwei Relationen R1 <strong>und</strong> R2 mit gleichen Schemata, sowie die Teilmengen<br />
A, B <strong>und</strong> C mit A := R1 − R2, B := R1 ∩ R2 <strong>und</strong> C := R2 − R1, dann<br />
folgt 3 :<br />
3 p = �<br />
D∈sch(R1)=sch(R2) R1.D = R2.D
3.5. SORTIERUNG UND DUPLIKATELIMINIERUNG 33<br />
R1<br />
✬<br />
✬<br />
A B C<br />
✫<br />
✫<br />
✩<br />
✪<br />
R2<br />
✩<br />
✪<br />
Operations- Operation Joindarstellung mit Vergleich<br />
ergebnis auf allen Attributen<br />
A R1 − R2 ¯⋉p(R1, R2)<br />
B R1 ∩ R2 ✶p (R1, R2) oder ⋉p(R1, R2)<br />
C R2 − R1 ¯⋉p(R2, R1)<br />
A, B R1 ❂⋊p(R1, R2) = R1<br />
A, C (R1 − R2) ∪ (R2 − R1) ⊲⊳p(R1, R2)<br />
B, C R2 ⋉❁p (R1, R2) = R2<br />
A, B, C R1 ∪ R2 ❂×❁p (R1, R2)<br />
Tabelle 3.1: Zusammenfassende Darstellung der Mengenoperationen<br />
3.5 Sortierung <strong>und</strong> Duplikateliminierung<br />
Bei den in diesem Kapitel vorgestellten Operatoren wird gr<strong>und</strong>sätzlich keine Duplikateliminierung<br />
vorgenommen. Dafür werden die folgenden Dup-Methoden eingeführt.<br />
Es können die schon im vorigen Abschnitt benutzten Methoden eingesetzt<br />
werden (aus diesem Gr<strong>und</strong> verzichten wir diesmal auf die Iteratoren, siehe<br />
dazu Kapitel 3.3). Dabei vergleicht die Brute-Force Methode NestedDup einfach<br />
analog zum NestedLoops-Join - in einer geschachtelten Schleife - jedes Tupel mit<br />
jedem anderen.
34 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
Liegt hingegen eine Sortierung vor, braucht die Eingabe lediglich von Anfang<br />
bis Ende einmal durchsucht <strong>und</strong> Doppelte gelöscht zu werden. Dieses Vorgehen<br />
wird SortDup genannt. Alternativ dazu kann auch ein Index benutzt werden, der<br />
entstehende Operator heißt IndexDup.<br />
Die Laufzeiten dieser Operatoren ergeben sich zu O(n 2 ) für den NestedDup <strong>und</strong><br />
O(n) für Sort- <strong>und</strong> IndexDup. Dabei ist natürlich für Sort- bzw. IndexDup das<br />
Vorliegen einer Sortierung bzw. eines Index Gr<strong>und</strong>voraussetzung.<br />
Es kann sinnvoll sein, Zwischenrelationen zu sortieren, wenn dadurch später z. B.<br />
durch Anwendung eines Merge-Joins anstelle eines NestedLoops-Joins die investierte<br />
Zeit wieder eingespart werden kann. Für die Sortierung ist der Operator<br />
SortX zuständig, dabei ist X der Sortierungsschlüssel. Die Sortierung hat bekanntlich<br />
eine Laufzeit von O(n · log(n)).<br />
3.6 Übersetzung der logischen Algebra<br />
In diesem Abschnitt werden die einzelnen Operatoren der logischen Algebra (Algebraische<br />
Optimierung) in eine äquivalente Darstellung der physischen Algebra<br />
(Physische Optimierung) übersetzt. Dabei werden zusätzliche Metadaten benutzt.<br />
In den eckigen Klammern stehen Operationen, die nur auszuführen sind,<br />
falls es notwendig ist. Wenn kein Index vorhanden ist, kann mittels Hash bzw.<br />
T ree ein Zugriffsschlüssel aufgebaut werden.<br />
Unäre Operatoren zeigen gr<strong>und</strong>sätzlich eine Komplexität von O(n), alle mit Sortierung<br />
verwandten Operatoren eine von O(n·log(n)) <strong>und</strong> die binären Operatoren<br />
letztlich höchstens eine Abschätzung von O(n 2 ).<br />
Bei der Übersetzung einer algebraischen Selektion in einen physischen Operator<br />
sollte man sich bewußt sein, daß das Lesen über einen Index eine Sortierung auf<br />
einem Attribut zur Folge haben kann, die für nachfolgende Operationen ausgenutzt<br />
werden könnte. Es ist daher sinnvoll, die Möglichkeit eine Indexselektion<br />
nicht von vornherein zu verwerfen, auch nicht, wenn dieses Attribut in der Selektionsbedingung<br />
nicht vorkommt. Daraus folgt, daß die Wahl des Zugriffs nicht<br />
lokal getroffen werden kann.<br />
Natürlich kann auch in anderen Fällen die beste Implementierung eines algebraischen<br />
Operators im allgemeinen nicht lokal gef<strong>und</strong>en werden. Beispielsweise<br />
kann ein Hash-Join billiger sein als ein Merge-Join, falls im letzteren Fall die<br />
entsprechenden Relationen noch sortiert werden müssen. Folgt jedoch ein Operator,<br />
der die Sortierung ebenfalls ausnutzen kann (z.B. ein weiterer Join oder eine<br />
Projektion mit Duplikatelimierung), so kann es günstiger sein, den Merge-Join<br />
anzuwenden <strong>und</strong> dafür vorher zu sortieren.
3.7. PHYSISCHE OPTIMIERUNGSMÖGLICHKEITEN 35<br />
Alg. Operator Physischer Operator Komplexität<br />
✶p (R1, R2) = ✶ NestedLoop<br />
p (R1, R2) O(n 2 )<br />
= ✶ NestedLoop<br />
p (R2, R1) O(n 2 )<br />
= ✶ MergeJoin<br />
p ([SortA]R1, [SortB](R2)) O(n), [O(n · log(n))]<br />
= ✶ IndexJoin<br />
p ([HashA|T reeA](R1), O(n), [O(n · log(n))]<br />
[HashB|T reeB](R2))<br />
= ✶ HashJoin<br />
p (R1, R2) O(k · n)<br />
σp(R) = σ Rel<br />
p (R) O(n)<br />
= σ Index<br />
p (R) O(n)<br />
πl(R) = [NestedDup](π Rel<br />
l (R)) O(n · log(n))<br />
= [SortDup][SortA](π Rel<br />
l (R)) O(n · log(n))<br />
= [IndexDup][HashA|T reeA](π Rel<br />
l (R)) O(n)<br />
Tabelle 3.2: Algebraische Operatoren <strong>und</strong> die dazugehörigen physischen Implementierungen<br />
3.7 Optimierungsmöglichkeiten mit physischen<br />
Operationen<br />
(a) Eine Möglichkeit, das Speichern von Zwischenrelationen zu vermeiden, stellt<br />
das sogenannte Pipelining dar. Die Idee dabei ist, das Ergebnis einer Operation,<br />
wenn möglich, nicht zwischenzuspeichern, sondern ein berechnetes<br />
Tupel direkt an die nächste Operation zu übergeben.<br />
Wird zum Beispiel auf eine Relation zunächst eine Selektion <strong>und</strong> dann eine<br />
Projektion angewandt, so bedeutet das Pipelining, daß ein Tupel der Relation,<br />
welches die Selektionsbedingung erfüllt, nicht in ein Zwischenergebnis<br />
geschrieben wird, sondern daß es direkt an die Projektion weitergereicht,<br />
auf die benötigten Attribute projiziert <strong>und</strong> erst dann gespeichert wird.<br />
Die Schreibweise hierfür ist 〈π ◦ σ〉. Hierzu sei bemerkt, daß, falls bei einem
36 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
Operator keine Spezifizierung wie Rel, Index, NestedLoop etc. angegeben<br />
wird, nicht der algebraische Operator gemeint ist, sondern ein beliebiger<br />
physischer Operator dieser Art.<br />
Dabei gibt es natürlich noch viele andere Operationenpaare, auf die das<br />
Pipelining anwendbar ist, hierbei seien insbesondere die folgenden genannt:<br />
• Pipelining von Selektion <strong>und</strong> Join:<br />
〈σ ◦ ✶〉(R1, R2) = σ(R1 ✶ R2)<br />
• Pipelining von NestedLoop-Join <strong>und</strong> Selektion:<br />
〈✶ NestedLoop ◦1 σ〉(R1, R2) = σ(R1) ✶ NestedLoop R2<br />
Dabei besagt der Index, auf welchen Operanden die Selektion anzuwenden<br />
ist, also auf welcher Eingabe das Pipelining stattfindet.<br />
• Pipelining von Merge-Join <strong>und</strong> Selektion:<br />
〈✶ MergeJoin ◦ [σ Rel<br />
p1 , σRel p2 ]〉(R1, R2) = σp1(R1) ✶ MergeJoin σp2(R2)<br />
Dabei ist die erste Selektion auf die linke Relation <strong>und</strong> die zweite auf<br />
die rechte anzuwenden.<br />
• Pipelining von Projektion <strong>und</strong> Join:<br />
〈π ◦ ✶〉(R1, R2) = π(R1 ✶ R2)<br />
• Pipelining von NestedLoop-Join <strong>und</strong> Projektion:<br />
〈✶ NestedLoop ◦1 π〉(R1, R2) = π(R1) ✶ NestedLoop R2<br />
Dabei besagt der Index, auf welchen Operanden die Projektion anzuwenden<br />
ist.<br />
• Pipelining von Merge-Join <strong>und</strong> Projektion:<br />
〈✶ MergeJoin ◦ [π Rel<br />
l1 , π Rel<br />
l2 ]〉(R1, R2) = πl1(R1) ✶ MergeJoin πl2(R2)<br />
Dabei ist die erste Projektion auf die linke Relation <strong>und</strong> die zweite auf<br />
die rechte anzuwenden.<br />
Darüberhinaus ist bei einstelligen Operationen ein Pipelining stets möglich.<br />
Außerdem können diese Pipelinings auch hintereinander ausgeführt werden.
3.8. HEURISTISCHE PHYSISCHE OPTIMIERUNG 37<br />
(b) Die Kosten für die Anfragebearbeitung werden wesentlich durch die Anzahl<br />
der Hintergr<strong>und</strong>speicherzugriffe bestimmt. Um diese gering zu halten,<br />
liegt es nahe, die Daten temporär komprimiert zu speichern. Hierbei kann<br />
man die bekannten verlustfreien Verfahren wie Huffman-Komprimierung<br />
(Lauflängenkomprimierung), Nullunterdrückung oder LZW benutzen.<br />
(c) Die Erzeugung von Zugriffspfaden kann unter bestimmten Umständen sinnvoll<br />
sein. Dazu gehören die Anfragen, die die Tabelleninformationen mehrmals<br />
direkt ansprechen <strong>und</strong> somit eine Indexierung sinnvoll machen, da sie<br />
einen log(n) Zugriff ermöglichen.<br />
(d) Vorhandene Sortierungen können für verschiedene Operatoren genutzt werden.<br />
Falls keine Sortierung existiert, kann es kostensparend sein, diese zu<br />
erzeugen.<br />
(e) Darüberhinaus können temporäre Zugriffspfade angelegt werden, die nicht<br />
permanent abgespeichert werden müssen. Dadurch können Zugriffe auf den<br />
externen Speicher verringert werden.<br />
3.8 Heuristische physische Optimierung<br />
In diesem Abschnitt soll der Ablauf beschrieben werden, der einen algebraisch optimierten<br />
Anfragebaum in einen physischen Prototypen verwandelt. Dabei dienen<br />
die Laufzeiten als maßgebliche Vorgabe zur Auswahl des zu ersetzenden Operators.<br />
Bildlich gesprochen wandert man von unten nach oben (bottom-up) durch den<br />
Baum <strong>und</strong> ersetzt an jedem Knoten den algebraischen Operator durch einen physischen,<br />
indem man die gegebenen Metadaten als Beurteilungskriterium nimmt. 4<br />
Mit Hilfe der in der Tabelle 3.2. zusammengefaßten physischen Operatoren, läßt<br />
sich wie folgt ein einfacher Algorithmus angeben:<br />
(a) Selektionen werden wie folgt ersetzt:<br />
(1) Selektionen werden durch Index-Selektionen ersetzt, falls ein Index auf<br />
dem gesuchten Attribut vorhanden ist (O(n)).<br />
(2) Wenn nicht, werden einfache Scan Rel<br />
p<br />
(b) Projektionen werden wie folgt ersetzt:<br />
benutzt (O(n)).<br />
(1) Projektionen werden durch Index-Projektionen ersetzt, falls ein passender<br />
Index vorhanden ist (O(n)).<br />
4 Folgende Heuristik ist vom Autor selbst entwickelt worden <strong>und</strong> wird später durch verschie-<br />
dene Beispiele als sinnvoll bestätigt.
38 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
(2) Wenn nicht, wird auf Sortiertheit der Relationen untersucht, um daraufhin<br />
eine SortedDup-Projektion zu benutzen (O(n · log(n))).<br />
(3) Schließlich werden nach dem Scheitern aller vorherigen Varianten die<br />
Relationen durch eine NestedDup-Projektion ersetzt (O(n · log(n))).<br />
(c) Verb<strong>und</strong>e werden wie folgt ersetzt:<br />
(1) Liegen die Relationen schon vorpartioniert im Hauptspeicher wird ein<br />
Hash-Join mit der Laufzeit von O(k · n) genommen.<br />
(2) Falls nicht, wird nach Indexen gesucht. Je nach gegebenem Index zu<br />
den einzelnen zu verbindenden Relationen wird ein Index-Join oder<br />
ein Index-Index-Join gewählt. (O(n) bzw. O(n · log(n)))<br />
(3) Wenn auch diese Metadaten nicht existieren, untersucht man die Relationen<br />
auf Sortiertheit. Sind beide Relationen sortiert, wird mittels<br />
eines Merge-Join verb<strong>und</strong>en (O(n)).<br />
(4) Als letzte zu benutzende Variante wird ein einfacher Nested-Loop-Join<br />
zur Verbindung der Relationen genommen (O(n 2 )).<br />
(d) Pipelining der Zwischenergebnisse:<br />
Wie im vorigen Abschnitt erwähnt, kann in Spezialfällen das Speichern der<br />
Zwischenergebnisse vermieden werden. Dabei reicht man die Einzelergebnisse<br />
gleich dem nächsten Operator weiter. Wir untersuchen deshalb den<br />
Anfragebaum auf diese genannten Beispiele <strong>und</strong> fügen das Pipelining des<br />
Zwischenergebnisses ein (siehe dazu Kapitel 3.7).<br />
(1) Untersuche den Anfragebaum auf Pipelining von Selektion bzw. Projektion<br />
<strong>und</strong> Join.<br />
(2) Untersuche den Anfragebaum auf Pipelining von NestedLoop-Join <strong>und</strong><br />
Selektion bzw. Projektion.<br />
(3) Untersuche den Anfragebaum auf Pipelining von Merge-Join <strong>und</strong> Selektion<br />
bzw. Projektion.
3.9. BEISPIEL FÜR DIE PHYSISCHE OPTIMIERUNG 39<br />
3.9 Beispiel für die physische Optimierung<br />
Das bekannte Beispiel aus dem Kapitel 2.2 kann jetzt physisch optimiert werden<br />
<strong>und</strong> erlangt schließlich - mit den gewonnenen Methoden - den bislang kostengünstigsten<br />
Zugriffsplan.<br />
Optimierung mittels Metadaten: Angenommen es gibt einen Index auf dem Attribut<br />
R1.A <strong>und</strong> einen auf R2.C, dann wäre eine Möglichkeit, die Beispielanfrage<br />
auszuführen, folgende:<br />
(1) Verwendung des R1.A-Index zur schnelleren Selektion von R1-Tupeln mit<br />
R1.A = c (1 Tupelzugriff, Ergebnisgröße=1)<br />
(2) Für jeden dadurch ermittelten R1.C-Wert verwende R2.C-Index zur Bestimmung<br />
der Joinpaare. (1 Tupelzugriff, Ergebnisgröße=1)<br />
(3) Eliminiere (σ(R1) ✶ R2)-Tupel mit E �= 2 (0 Tupelzugriffe, Ergebnisgröße=1)<br />
(4) Projeziere auf B, D. (0 Tupelzugriffe, Ergebnisgröße=1)<br />
Dabei soll auf dem linken Pfad von σA=c bis πB,D ein Pipelining genutzt werden.<br />
Daraus ergibt sich der Anfragebaum:<br />
π Rel<br />
B,D<br />
σ Rel<br />
E=2<br />
✶ Rel,Index<br />
R1.C=R2.C<br />
✪ ❡<br />
✪ ❡<br />
✪<br />
✪<br />
σ Index<br />
A=c<br />
R1<br />
Abbildung 3.12: physisch optimierter Anfragebaum<br />
Durch die Hilfe der Meta-Daten werden kaum noch Tupel gebraucht. Die Anfrage<br />
greift somit direkt auf die 2 benötigten Tupel zu; insgesamt sind also 2<br />
Tupelzugriffe nötig.<br />
R2
40 KAPITEL 3. PHYSISCHE OPTIMIERUNG<br />
3.10 Fazit der physischen Optimierung<br />
In den meisten Büchern (darunter Kemper [29]) werden die in diesem Kapitel<br />
vorgestellten Iteratoren - der verschiedenen physischen Operatoren - nicht genau<br />
genug beschrieben. Deshalb wurde viel Wert darauf gelegt, diese ungenau <strong>und</strong><br />
teilweise auch falsch beschriebenen Abläufe, neu zu konstruieren.<br />
Auch die Darstellung der Mengenoperationen durch Verb<strong>und</strong>operatoren werden<br />
in der Literatur stiefmütterlich behandelt. Deshalb wurde eine neue Form der<br />
Präsentation gewählt, die die spätere Implementierung stark vereinfachen sollte<br />
(siehe dazu Tabelle 3.1).<br />
Schließlich wurde das Hauptaugenmerk auf die Entwicklung einer physischen Heuristik<br />
zur Schaffung eines physischen Prototypen gelegt. Sie basiert größtenteils<br />
auf dem Zusatzwissen, welches aus den Metadaten der gegeben Ausgangsrelationen<br />
gewonnen wird. Der reine Ablauf ist durch mehrere Tests mit verschiedenen<br />
Beispielen gewonnen worden <strong>und</strong> sollte nicht als einzig richtig bzw. möglich betrachtet<br />
werden.
Kapitel 4<br />
Kostenfunktionen <strong>und</strong><br />
Selektivitäten<br />
Ordnung braucht nur der Dumme,<br />
das Genie beherrscht das Chaos.<br />
Albert Einstein<br />
Optimierungssysteme, die auf f<strong>und</strong>ierten Heuristiken basieren, liefern in der Mehrzahl<br />
der Fälle - innerhalb kurzer Zeit - nahezu optimale Anfrageauswertungspläne.<br />
Leider generieren solche Heuristiken auch schlechte Prototypen, die durch ein<br />
sinnvolles Kostenmodell dann ausgeschlossen werden müssen. Dabei werden verschiedene<br />
Anfragepläne miteinander verglichen, <strong>und</strong> der Beste wird dann gewählt.<br />
Ein Kostenmodell bietet eine Funktion, die die Laufzeit der Operatoren der physischen<br />
Algebra abschätzt. Dazu werden diverse Parameter benötigt, wie zum<br />
Beispiel Indexe, Cluster, Kardinalitäten <strong>und</strong> Verteilungen. Anfangs muß die Anzahl<br />
der im Berechnungsprozeß benötigten Tupel durch sinnvolle Funktionen abgeschätzt<br />
<strong>und</strong> verglichen werden.<br />
Indexinformationen<br />
algebraischer Ausdruck<br />
✲<br />
DB-Kardinalitäten<br />
❄<br />
✻<br />
Kostenmodell<br />
Clusterinformationen<br />
❄<br />
✲ Ausführungskosten<br />
✻<br />
Attributverteilung<br />
Abbildung 4.1: Aufbau eines Kostenmodells<br />
Die Ausgabe der Ausführungskosten entsteht aus mehreren Größen, die dem Zeitoder<br />
Platzbedarf für die Anfrageausführung zugeordnet sind. Für den Zeitbedarf<br />
sind die Kosten für Zugriffe auf den externen Speicher, für Tupelzugriffe <strong>und</strong><br />
natürlich den CPU-Berechnungsaufwand entscheidend. Für den Platzbedarf ist<br />
die Gesamtgröße aller im Speicher zu haltenden Relationen wichtig.<br />
41
42 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
4.1 Allgemeine Kostenberechnung<br />
Der Suchraum der Planoptimierungen wird in der Literatur mit B bezeichnet.<br />
Dabei wird folgende Definition der allgemeinen Kostenberechnung vorgenommen:<br />
Definition 4.1.1 Für jeden Anfrageplan P existiert eine Kostenabschätzung bezüglich<br />
der Zugriffsmethoden <strong>und</strong> -reihenfolgen auf die jeweiligen Operanden;<br />
diese wird mit cost(P) bezeichnet. Zielsetzung ist dann die Generierung des optimalen<br />
Ausführungsplanes Popt ∈ B, so daß gilt:<br />
cost(Popt) = min(P∈B) cost(P)<br />
Die Abschätzung der Zugriffskosten cost(P) eines vollständigen Ausführungsplanes<br />
P werden aus den einzelnen Kostenfunktionen für dessen Blätter bzw. interne<br />
Knoten inkrementell berechnet. Dabei gehen die gewählten Zugriffsmethoden, die<br />
Zugriffsreihenfolge <strong>und</strong> die Selektivitäten der Prädikate als Parameter in die Kostenberechnungen<br />
ein.<br />
Letztendlich werden die gesamten Kosten eines Ausführungsplanes dadurch ermittelt,<br />
daß man rekursiv die Kosten für jeden einzelnen Knoten Ki, i = 1, . . . , n<br />
berechnet <strong>und</strong> sie dann zu einem Wert zusammenaddiert:<br />
cost(P) =<br />
n�<br />
cost(Ki)<br />
i=1<br />
Dafür benötigt man die Kosten jedes einzelnen Knotens, die wiederum von der<br />
dort auszuführenden Operation <strong>und</strong> den Relationen, auf denen sie angewendet<br />
werden, abhängig sind.<br />
Die Kostenfunktionen sind maßgebend für die Auswahl möglicher Alternativen<br />
der Implementierung von Zugriffsmethoden. In den folgenden Abschnitten werden<br />
deshalb die benötigten Funktionen <strong>und</strong> Abschätzungen vorgestellt, die später als<br />
Basis unserer Bewertung eines Ausführungsplanes dienen werden.<br />
4.2 Kostenfunktion<br />
Die in dieser Diplomarbeit verwendeten Formeln zur Abschätzung der Ausführungspläne,<br />
basieren auf dem Kostenmodell von Selinger, Astraham, Chamberlin,<br />
Lorie <strong>und</strong> Price [51]. Gr<strong>und</strong> dafür sind die präzisen Abschätzungen, die maßgeblich<br />
den Optimierungsprozeß beeinflussen.
4.2. KOSTENFUNKTION 43<br />
Die jeweiligen Formeln zur konkreten Kostenabschätzung der unterstützten Zugriffsmethoden<br />
sind in den nachfolgenden Tabellen zusammengefaßt. Dabei werden<br />
zunächst die üblichen statistischen Annahmen bezüglich der Gleichverteilung<br />
der Werte über den Wertebereich eines Attributes <strong>und</strong> bezüglich der Unabhängigkeit<br />
zwischen den Werten verschiedener Attribute getroffen.<br />
Um zu einer zuverlässigen Abschätzung der Zugriffskosten cost(P) eines Ausführungsplanes<br />
P zu kommen, müssen diejenigen Ressourcen in den Kostenfunktionen<br />
berücksichtigt werden, welche die Gr<strong>und</strong>lage der Anfrageausführung darstellen.<br />
Hierzu gehört der externe Speicherbedarf, die Anzahl der Disk-I/O-Zugriffe,<br />
der im Systembuffer benötigte Speicherplatz, sowie die notwendige CPU-Zeit zur<br />
Ausführung eines spezifischen Ausführungsplanes.<br />
Deshalb müssen sinnvolle Kostenmodelle sowohl die CPU-Kosten als auch die<br />
I/O-Kosten gleichzeitig betrachten. Dieser Sachverhalt wird nicht nur durch alle<br />
Kostenmodelle in realisierten Datenbankmanagement-Systemen bestärkt, sondern<br />
ist in der Theorie auch fester Bestandteil aller Untersuchungen. Siehe dazu<br />
insbesondere [51].<br />
Vor einem Jahrzehnt fiel der größte Berechnungsaufwand im Zugriffs- <strong>und</strong> Speichersystem<br />
an. Dazu wurde dieser Wert für vereinfachte Kostenmodelle als Gr<strong>und</strong>lage<br />
der Kostenberechnung benutzt. Natürlich wurde dabei ein Fehler in Kauf genommen,<br />
der sich abhängig von der CPU-Belastung vergrößerte oder relativierte.<br />
Heutige Systeme besitzen immer größer werdende Caches (viele I/O-Zugriffe werden<br />
vermieden), welche zusätzlich berücksichtigt werden müssen, da dadurch die<br />
CPU-Kosten einen höheren Anteil an den Gesamtkosten erlangen.<br />
Inzwischen sind verschiedene Verfeinerungen vorgenommen worden, die eine bessere<br />
Kosteneinschätzung ermöglichen sollen. Verschiedene neue Ansätze lassen<br />
sich bei Mitschang [36] nachlesen. Dabei versucht man zum Beispiel zusätzliche<br />
Faktoren in das Kostenmodell aufzunehmen wie z.B. die Zerlegung der I/O-<br />
Kosten in Input- <strong>und</strong> separate Output Kosten.<br />
Bei dem eigens für diese Diplomarbeit entwickelten Kostenmodell werden basierend<br />
auf der Doktorarbeit von Utesch (siehe dazu [54]) die I/O-Kosten ausschließlich<br />
als Input-Kosten gesehen, da die gesamten I/O-Kosten dieselbe Größenordnung<br />
haben, wie die Input-Kosten allein. Dies ist der Fall, da nach jeder Operation<br />
das Zwischenergebnis in den Speicher geschrieben wird <strong>und</strong> danach wieder für die<br />
nächste Operation gelesen werden muß. Diese beiden Schritte benötigen in etwa<br />
denselben Aufwand.<br />
Zusätzlich dazu bietet dieses Kostenmodell auch eine leicht verständliche <strong>und</strong><br />
somit einfache Erweiterungsmöglichkeit für spätere Verfeinerungen.
44 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
Seien<br />
R, R1, R2, . . . Relationen<br />
p = AΘB Prädikat, Θ ∈ {, ≥, =, �=}<br />
A, B Attribute oder Konstanten,<br />
|R| Kardinalität von R in Tupeln,<br />
page(R) Kardinalität von R in Seiten (Blöcken),<br />
n(A, R) Anzahl der verschiedenen Werte des Attributes A in R,<br />
Index(R) Index einer Relation R,<br />
IndexA(R) Index einer Relation R auf dem Attribut A,<br />
|Index(R)| Tiefe des Indexbaumes von Index(R),<br />
page(Index(R)) Kardinalität von Index(R) in Seiten (Blöcken),<br />
max(A), min(A) Maximum bzw. Minimum der Werte des Attributes A,<br />
dom(A) Wertebereich des Attributes A,<br />
q(σp(R)) Selektivität der Selektion σp(R) bezüglich des Prädikats p,<br />
q(R1 ✶p R2) Selektivität des Verb<strong>und</strong>es R1 ✶p R2<br />
bezüglich des Prädikats p.<br />
Definition 4.2.1 Die Abschätzung der Zugriffskosten für eine Relation R lautet:<br />
Zugriffskosten(R) = (I/O-Kosten) + W · (CPU-Kosten)<br />
wobei W : Gewichtung von CPU- zu I/O-Kosten; abhängig von Systemkonfiguration<br />
(Hard- <strong>und</strong> Software).<br />
Um die beiden stark unterschiedlichen Kostenanteile zu den gesamten Zugriffskosten<br />
zusammenfassen zu können, werden die CPU-Kosten bezüglich der I/O-<br />
Kosten normiert. Dies erfolgt durch den Proportionalitätsfaktor W , der das durchschnittliche<br />
Verhältnis des Aufwandes für einen Aufruf des Zugriffssystems zu<br />
einem Seitenzugriff auf den externen Speicher angibt.<br />
Durch Festlegung des Proportionalitätsfaktors W kann man somit das Kostenmodell<br />
an eine vorhandene Rechnerkonfiguration anpassen, um sinnvolle Aussagen<br />
zu erreichen.<br />
Natürlich gibt es dabei zwei Extremfälle, die in diesem Modell berücksichtigt<br />
werden können [51]:<br />
(a) CPU-Constraints<br />
Ist die CPU der Engpaß des Systems, sind natürlich I/O-Zugriffe zu bevorzugen.<br />
Somit werden rechenintensive Ausführungspläne bei der Optimierung<br />
benachteiligt. Für eine I/O-Operation werden lediglich die hierzu<br />
auszuführenden Instruktionen angesetzt. Dabei wird angenommen, daß die<br />
Zugriffszeit voll überlappend zur weiteren Verarbeitung ausgenutzt werden<br />
kann.
4.3. SELEKTIVITÄT VON PRÄDIKATEN 45<br />
W =<br />
#Instruktionen pro CPU-Operation<br />
#Instruktionen pro I/O-Operation<br />
In der Praxis zeigt sich, daß für W folgendes gilt: 0, 1 ≤ W ≤ 0, 4.<br />
(b) I/O-Constraints<br />
Normalerweise sind es die I/O-Kosten, die Zugriffe, die am meisten Zeit<br />
benötigen. Deshalb sollte man rechenintensive Ausführungspläne bevorzugen.<br />
Somit wird in diesem Fall zusätzlich zu den oben genannten Anteilen,<br />
die volle Zugriffszeit für eine I/O-Operation angerechnet, bei dem sich die<br />
MIPS-Rate des verwendeten Rechners für eine durchschnittliche Zugriffszeit<br />
bestimmen läßt.<br />
W =<br />
# Instruktionen pro CPU-Operation<br />
(# Instruktionen pro I/O-Operation) + (Zugriffszeit · MIPS-Rate)<br />
Erfahrungswerte zeigen, daß man mit einem W < 0, 01 den besten Plan<br />
generieren kann.<br />
Folgendes Beispiel verdeutlicht wie einfach die Berechnung des Proportionalitätsfaktors<br />
W ist:<br />
1000 Instruktionen pro CPU-Operation<br />
2500 Instruktionen pro I/O-Operation<br />
30ms Durchschnittliche Zugriffszeit<br />
10 7 MIPS-Rate (Instruktionen pro Sek<strong>und</strong>e)<br />
Somit ergibt sich bei einem vorliegendem CPU-Constraint:<br />
Schließlich für einen I/O-Constraint:<br />
W =<br />
W = 1000 1<br />
=<br />
2500 4<br />
1000<br />
(2500 + 30 · 10 −3 · 10 7 )<br />
= 0, 4<br />
= 2<br />
605<br />
4.3 Selektivität von Prädikaten<br />
≈ 0, 0033<br />
Von f<strong>und</strong>amentaler Bedeutung für die Kostenberechnung ist die Frage, wieviele<br />
Tupel sich bei Auswertung einer Bedingung qualifizieren, da erst dadurch eine<br />
Abschätzung der Größe von Zwischenergebnissen ermöglicht wird. Das führt zur<br />
Einführung des Begriffes Selektivität (Auswahlschärfe), also dem Anteil der sich<br />
qualifizierenden Tupel.
46 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
Definition 4.3.1<br />
(a) Für eine Selektionsbedingung p auf einer Relation R sei die Selektivität<br />
q(p) definiert als das Verhältnis der Tupel, die diese Bedingung erfüllen zu<br />
den gesamten Tupeln der Relation.<br />
(b) Für eine Joinbedingung p der beiden Relationen R1 <strong>und</strong> R2 sei die Selektivität<br />
q(p) definiert als das Verhältnis der Tupel, die diese Joinbedingung<br />
erfüllen zu den Tupeln des Kartesischen Produktes der beiden Relationen.<br />
Für den Fall, daß A ein gleichverteiltes Attribut von R <strong>und</strong> a eine Konstante im<br />
Wertebereich von A ist, ergibt sich die Selektivität einer Bedingung AΘa als:<br />
⎧<br />
für Θ ≡ =<br />
⎪⎨<br />
q(AΘa) :=<br />
⎪⎩<br />
1<br />
n(A,R)<br />
max(A)−a<br />
max(A)−min(A)<br />
a−min(A)<br />
max(A)−min(A)<br />
1 − 1<br />
n(A,R)<br />
für Θ ≡ > oder Θ ≡ ≥<br />
für Θ ≡ < oder Θ ≡ ≤<br />
für Θ ≡ �=<br />
Das heißt, ein Prädikat p = AΘa mit einer Selektivität q(AΘa) liefert, wenn<br />
angewandt auf die zugehörige Relation R mit der Kardinalität |R|, ein Zwischenergebnis<br />
der Größe q(AΘa) · |R|.<br />
Darüberhinaus ist für viele Selektivitätsabschätzungen die Anzahl der verschiedenen<br />
Werte eines Attributes einer Relation eine wichtige Größe. Deshalb muß<br />
außer der Größe der Ergebnisrelation R einer Operation auch n(A, R) abgeschätzt<br />
werden.<br />
Unter der Annahme, daß die in den Prädikaten p, p1, p2 auftauchenden Attribute<br />
gleichverteilt sind, ergeben sich folgende Selektivitätsabschätzungen, wobei<br />
a, a1, . . . , an ∈ dom(A) paarweise verschiedene Konstanten sind (siehe Tabelle<br />
4.1) 1 :<br />
4.3.1 Selektivität von Projektion <strong>und</strong> Selektion<br />
Da die Projektion πA gemäß ihrer Definition der Auswahl eines Attributes A<br />
entspricht, ist somit die Anzahl der verschiedenen Werte n(A, R) in der Relation<br />
R gleich der Kardinalität von πA(R).<br />
1 Bei einer Unteranfrage gilt die genannte Formel nur dann, wenn die A-Werte des Ergebnisses<br />
in dom(A) liegen.
4.3. SELEKTIVITÄT VON PRÄDIKATEN 47<br />
Prädikat p Abschätzung Standardwert<br />
A = a<br />
A {>, ≥} a<br />
A { a1) ∧ (A < a2)<br />
A ∈ {a1, . . . , an}<br />
A ∈ (Unteranfrage) 1<br />
1<br />
10<br />
1<br />
3<br />
1<br />
3<br />
q(p1) + q(p2)<br />
−q(p1) · q(p2) -<br />
a2−a1<br />
max(A)−min(A)<br />
n<br />
n(A,R)<br />
|Unteranfrage|<br />
n(A,R)<br />
Tabelle 4.1: Selektivitätsabschätzung für Prädikate<br />
n(A, R) = |πA(R)|<br />
Die Selektivität einer Selektion der Form σp(R) ist definiert durch:<br />
q(σp(R)) := |σp(R)|<br />
|R|<br />
Sie gibt das Verhältnis der Tupelanzahl von Resultat <strong>und</strong> Original an. Daraus<br />
folgt dann auch:<br />
0 ≤ q(σp(R)) ≤ 1<br />
Das heißt, das Ergebnis einer Selektion enthält maximal soviel Tupel wie das<br />
Original. Allgemein gilt:<br />
q(σp(R)) = q(p)<br />
Man geht nun davon aus, daß im Mittel die Selektivität wesentlich kleiner als<br />
1 ist, so daß die Kosten einer Anfrageauswertung (bemessen an der Anzahl zu<br />
1<br />
4<br />
1<br />
2<br />
1<br />
2
48 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
bewegender Tupel) desto geringer ist, je früher die Selektion angewandt wird.<br />
Falls das Attribut, nach dem selektiert wird, ein Schlüssel ist oder die Werte des<br />
Attributes gleichverteilt sind, ergeben sich folgende Werte für die Selektivität:<br />
Die Selektivität der Operation σR.A=a(R), also des Vergleiches des Attributs A<br />
, falls A ein Schlüssel ist.<br />
aller Tupel von R mit der Konstanten a, beträgt 1<br />
|R|<br />
Die Selektivität der Operation σ(R.A=a)(R) ist bei einer Gleichverteilung der Werte<br />
1<br />
von R.A gleich , da in diesem Fall<br />
n(A,R)<br />
q(p) = q(R.A = a) =<br />
1<br />
n(A, R)<br />
ist. Dabei ist der obige Fall ein Spezialfall hiervon, da für ein Schlüsselattribut A<br />
einer Relation R n(A, R) = |R| gilt.<br />
Wird eine Selektion durchgeführt, auf die diese Spezialfälle nicht zutreffen, müssen<br />
andere Methoden angewandt werden, um trotzdem Werte für die Selektivität zu<br />
erhalten. Mit diesen Methoden beschäftigt sich der Abschnitt 4.4.<br />
Zur Berechnung der Selektivität benötigt man Abschätzungen für n(A, R1) für<br />
jedes Attribut A, wobei R1 = σp(R2) sei. Von der Ausgangsrelation R2 sei ihre<br />
Größe mit n bezeichnet <strong>und</strong> m = n(A, R2) bekannt. Um nun eine Abschätzung<br />
für n(A, R1) angeben zu können, müssen zwei Fälle unterschieden werden: Der<br />
Fall, daß das Attribut A nicht in der Bedingung p auftritt <strong>und</strong> der, bei dem A in<br />
der Bedingung p vorkommt.<br />
• Für den ersten Fall sei r die geschätzte Kardinalität von R1. Dabei läßt sich<br />
das Problem, bei angenommener Gleichverteilung <strong>und</strong> Unabhängigkeit der<br />
Attribute, auf die folgende kombinatorische Fragestellung zurückführen:<br />
Gegeben sei eine Urne mit n Kugeln in m verschiedenen Farben. Wieviele<br />
verschiedenfarbige Kugeln erhält man, wenn man r Kugeln zufällig ohne<br />
Zurücklegen aus der Urne zieht?<br />
Eine Abschätzung für diese Anzahl ergibt sich nach Ceri [8] zu:<br />
⎧<br />
r<br />
⎪⎨<br />
falls r <<br />
n(A, R1) =<br />
m<br />
2<br />
≤ r < 2 · m<br />
r+m<br />
m falls 3 2<br />
⎪⎩<br />
m falls r ≥ 2 · m<br />
• Für den Fall, daß A in der Selektionsbedingung p auftritt, erhält man<br />
abhängig von der Art des Auftretens von A verschiedene Formeln. Für den<br />
Spezialfall p = (A = a) mit konstantem a bekommen wir n(A, R1) = 1.
4.3. SELEKTIVITÄT VON PRÄDIKATEN 49<br />
Betrachtet man eine Projektion R1 = πl(R2), dann gilt n(A, R1) = n(A, R2) für<br />
alle Attribute A der Ergebnisrelation.<br />
4.3.2 Selektivität eines Joins<br />
Wir wollen jetzt die oben beschriebenen Parameter verwenden, um eine Abschätzung<br />
des natürlichen Verb<strong>und</strong>es bzw. eine Abschätzung für die Größe des Ergebnisses<br />
einer Equijoin-Operation berechnen zu können.<br />
• Ist sch(R1) ∩ sch(R2) = ∅, so entartet der natürliche Verb<strong>und</strong> zu einem<br />
Kartesischen Produkt, d.h. es gilt |R1 ✶ R2| = |R1| · |R2|.<br />
• Falls sch(R1) ∩ sch(R2) einen Schlüssel etwa für R1 enthält, so existiert<br />
zu jedem Tupel aus R2 höchstens ein Verb<strong>und</strong>-Partner in R1. Hieraus folgt<br />
|R1 ✶ R2| ≤ |R2|.<br />
• Sei sch(R1) ∩ sch(R2) = {A} <strong>und</strong> a ∈ dom(R1.A). Dann gibt es bei Gleich-<br />
|R2|<br />
verteilung ca. n(A,R2) Tupel in R2 mit dem A-Wert a; das gleiche gilt für<br />
alle weiteren auftretenden R1.A-Werte. Hieraus folgt:<br />
|R1 ✶ R2| ≤ n(A, R1) · |R2|<br />
n(A, R2)<br />
≤ |R1| · |R2|<br />
n(A, R2)<br />
Da der Join kommutativ ist, spielen die beiden Operanden dieselbe Rolle;<br />
von daher gilt die Formel auch mit vertauschten Relationen:<br />
|R1 ✶ R2| ≤ n(A, R2) · |R1|<br />
n(A, R1)<br />
≤ |R1| · |R2|<br />
n(A, R1)<br />
Damit gilt insgesamt<br />
�<br />
n(A, R2) · |R1|<br />
|R1 ✶ R2| ≤ min<br />
,<br />
n(A, R1)<br />
n(A, R1)<br />
�<br />
· |R2|<br />
n(A, R2)<br />
• Sei nun sch(R1) ∩ sch(R2) = {A1, . . . , As} mit s ≥ 2 <strong>und</strong> gleichverteilten<br />
<strong>und</strong> unabhängigen Attributen Ai. Dann erhält man das Ergebnis des<br />
Equijoins wie folgt:<br />
R1 ✶ R2 = σ (�s<br />
i=2 A(1)<br />
i =A(2)<br />
i ) R1 ✶R1.A1=R2.A1 R2,<br />
dabei sei A (j)<br />
i , i = 2, . . . , s, j = 1, 2, das Attibut der Relation definiert als<br />
R := R1 ✶R1.A1=R2.A1 R2, welches aus Rj.Ai entstanden ist. Die Größe des<br />
Zwischenergebnisses R läßt sich nach obigen Überlegungen mit<br />
�<br />
n(A1, R2) · |R1|<br />
m1 := min<br />
,<br />
n(A1, R1)<br />
n(A1,<br />
�<br />
R1) · |R2|<br />
n(A1, R2)
50 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
abschätzen. Die Größe des Endergebnisses läßt sich dann mit<br />
�<br />
s�<br />
m1 · q A (1)<br />
�<br />
i = A (2)<br />
i ) = m1<br />
s�<br />
· q(A (1)<br />
i<br />
i=2<br />
i=2<br />
abschätzen, wobei qi die Selektivität von A (1)<br />
i<br />
= A(2) i ) =: m1<br />
= A(2)<br />
i<br />
Da die Attribute Ai unabhängig sind, ergibt sich eine Abschätzung für<br />
q2 wie folgt. Es gibt in R maximal n(A2, Ri) verschiedene A (i)<br />
2 -Werte, al-<br />
so n(A (i)<br />
2 , R) ≤ n(A2, Ri). Das Ergbnis der Selektion σ (1)<br />
A 2 =A(2)<br />
2<br />
sei.<br />
s�<br />
i=2<br />
qi<br />
wird de-<br />
sto größer, je größer der Durchschnitt dom(A (1)<br />
2 ) ∩ dom(A (2)<br />
2 ) ist. Sei also<br />
o.B.d.A. dom(A (1)<br />
2 ) ⊂ dom(A (2)<br />
2 ), insbesondere also<br />
min(n(A (1)<br />
2 , R), n(A (2)<br />
2 , R)) = n(A (1)<br />
2 , R).<br />
Die Wahrscheinlichkeit dafür, daß ein Tupel r in R die Bedingung A (1)<br />
2 =<br />
A (2)<br />
2 erfüllt, ergibt sich zu<br />
n(A (1)<br />
2 , R)<br />
n(A (1)<br />
2 , R) · n(A (2)<br />
2 , R)<br />
min(n(A(1) 2 , R), n(A<br />
= (2)<br />
2 , R))<br />
.<br />
n(A (1)<br />
2 , R) · n(A (2)<br />
2 , R)<br />
Betrachtet man nämlich das Paar (r.A (1)<br />
2 , r.A (2)<br />
2 ) =: (a (1)<br />
2 , a (2)<br />
2 ), so gibt es<br />
n(A (1)<br />
2 , R) · n(A (2)<br />
2 , R) verschiedene Paare dieser Form. Ein solches Paar<br />
erfüllt die Selektionsbedingung, falls a (1)<br />
2 = a (2)<br />
2 gilt, die Anzahl dieser Tupel<br />
ist min(n(A (1)<br />
2 , R), n(A (2)<br />
2 , R)) = n(A (1)<br />
2 , R).<br />
Die Wahrscheinlichkeit berechnet sich dann als der Quotient der Anzahl<br />
der sich qualifizierenden Tupel <strong>und</strong> der Anzahl der gesamten Tupel, wegen<br />
der Gleichverteilungs- <strong>und</strong> Unabhängigkeitsannahme ist dieser genau der<br />
obengenannte Quotient, der sich noch wie folgt vereinfachen läßt.<br />
q2 = min(n(A(1) 2 , R), n(A (2)<br />
2 , R))<br />
n(A (1)<br />
2 , R) · n(A (2)<br />
2 , R) =<br />
1<br />
max(n(A (1)<br />
2 , R), n(A (2)<br />
2 , R)) .<br />
Induktiv kann man nun folgern, daß diese Formel auch für die anderen<br />
Attribute Ai gilt, d.h.<br />
qi = min(n(A(1)<br />
i , R), n(A (2)<br />
i , R))<br />
1<br />
max(n(A (1)<br />
i , R), n(A (2)<br />
i , R))<br />
n(A (1)<br />
i , R) · n(A (2)<br />
i , R) =<br />
∼ =<br />
1<br />
max(n(Ai, R1), n(Ai, R2)) ,
4.3. SELEKTIVITÄT VON PRÄDIKATEN 51<br />
da die im Fall i = 2 benutzten Abschätzungen auch in jedem weiteren<br />
Schritt gültig sind. Insgesamt gilt also<br />
s�<br />
|R1 ✶ R2| = m1 qi ∼ =<br />
�<br />
n(A1, R2) · |R1|<br />
min<br />
,<br />
n(A1, R1)<br />
n(A1,<br />
�<br />
R1) · |R2|<br />
n(A1, R2)<br />
i=2<br />
·<br />
s�<br />
i=2<br />
1<br />
max(n(Ai, R1), n(Ai, R2)) ,<br />
wobei das Attribut A1 frei gewählt werden kann, d.h. man könnte hier noch<br />
das Minimum über alle Permutationen der Menge {A1, . . . , As} bilden.<br />
Darüberhinaus wird die Selektivität, d. h. die Größe des Ergebnisses relativ zur<br />
Kardinalität des Kreuzproduktes, angegeben. Im allgemeinen gilt q(R1 ✶p R2) =<br />
q(p), also folgt:<br />
q(R1 ✶ R2) = |R1 ✶ R2|<br />
|R1 × R2| = |R1 ✶ R2|<br />
|R1| · |R2|<br />
mit<br />
0 ≤ q(R1 ✶ R2) ≤ 1.<br />
Das heißt in den obigen vier Fällen gilt:<br />
• sch(R1) ∩ sch(R2) = ∅:<br />
q(R1 ✶ R2) = |R1| · |R2|<br />
|R1| · |R2|<br />
= 1<br />
• sch(R1) ∩ sch(R2) enthält den Schlüssel A der Relation R1:<br />
• sch(R1) ∩ sch(R2) = {A}:<br />
q(R1 ✶ R2) ≤<br />
q(R1 ✶ R2) ≤<br />
|R2|<br />
|R1| · |R2|<br />
= 1<br />
|R1|<br />
�<br />
n(A,R2)·|R1|<br />
min<br />
, n(A,R1) n(A,R1)·|R2|<br />
�<br />
n(A,R2)<br />
|R1| · |R2|<br />
�<br />
n(A, R2)<br />
min<br />
n(A, R1) · |R2| ,<br />
�<br />
n(A, R1)<br />
n(A, R2) · |R1|<br />
• sch(R1) ∩ sch(R2) = {A1, . . . , As}:<br />
�<br />
n(A1,R2)·|R1|<br />
min<br />
, n(A1,R1)<br />
q(R1 ✶ R2) ≤<br />
n(A1,R1)·|R2|<br />
�<br />
· n(A1,R2)<br />
�s i=2<br />
|R1| · |R2|<br />
�<br />
n(A,R2)<br />
min n(A,R1)·|R2| ,<br />
�<br />
n(A,R1)<br />
n(A,R2)·|R1|<br />
�s i=2 max(n(Ai, R1), n(Ai, R2))<br />
=<br />
1<br />
max(n(Ai,R1),n(Ai,R2))<br />
=
52 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
In dem zweiten Fall, daß bei einem Equijoin (R1 ✶(R1.A=R2.A) R2) das Attribut<br />
A Schlüsseleigenschaften besitzt, kann die Größe des Ergebnisses mit |R2| <strong>und</strong><br />
1<br />
damit die Selektivität mit abgeschätzt werden. Denn jedes Tupel aus R2<br />
|R1|<br />
findet nur maximal einen Joinpartner. Ist A zusätzlich Fremdschlüssel für R2, so<br />
ist die Selektivität q(R1 ✶ R2) = 1<br />
|R1| .<br />
Setzt man voraus, daß die statistischen Werte eines DBMS immer auf dem aktuellen<br />
Stand gehalten werden, kann man im Prinzip die Reihenfolge eines sinnvollen<br />
Verb<strong>und</strong>es ermitteln, da es wegen kleinerer Zwischenergebnisse für R1 ✶ R2 ✶ R3<br />
am besten wäre, wenn man (R1 ✶ R2) ✶ R3 berechnet, wenn R1 ≤ R2 ≤ R3 ist.<br />
Auch beim Join benötigen wir eine Abschätzung für die Anzahl der verschiedenen<br />
Attribute in den gegebenen Relationen. Dafür wird folgender Ansatz genutzt:<br />
Es sei R1 = R2 ✶p R3, von den Ausgangsrelationen R2, R3 seien die Größen<br />
mit n2, n3 bezeichnet <strong>und</strong> mi = n(A, Ri) bekannt. Um nun eine Abschätzung für<br />
n(A, R1) angeben zu können, müssen zwei Fälle unterschieden werden, der Fall,<br />
daß das Attribut A nicht in der Bedingung p auftritt <strong>und</strong> der, bei dem A in der<br />
Bedingung p vorkommt.<br />
• Für den ersten Fall sei r die geschätzte Kardinalität von R1 <strong>und</strong> o.B.d.A.<br />
A ein Attribut von R2. Dann gilt genau wie bei der Selektion:<br />
⎧<br />
r<br />
⎪⎨<br />
falls r <<br />
n(A, R1) =<br />
m2<br />
2<br />
falls ≤ r < 2 · m2<br />
r+m2<br />
3<br />
m2<br />
2<br />
⎪⎩<br />
m2 falls r ≥ 2 · m2<br />
• Für den Fall, daß A in der Joinbedingung p auftritt, erhält man abhängig<br />
von der Art des Auftretens von A verschiedene Formeln. Für den Spezialfall<br />
p = (R2.A = R3.A) bekommen wir n(A, R1) ≤ min(n(A, R2), n(A, R3)).<br />
Ebenso kann man für einen Equijoin mit sch(R2)∩sch(R3) = {A1, . . . , As},<br />
d.h. p = � s<br />
i=1 R2.Ai = R3.Ai, für jedes der Attribute Ai die Abschätzung<br />
n(A, R1) ≤ min(n(Ai, R2), n(Ai, R3))<br />
nutzen. Im allgemeinen Fall kann man zumindestens noch die folgende<br />
Abschätzung angeben:<br />
n(A, R1) ≤ n(A, R2) + n(A, R3)
4.4. SELEKTIVITÄTSABSCHÄTZUNGEN 53<br />
4.4 Selektivitätsabschätzungen<br />
In den obigen Abschnitten wurden nur in Spezialfällen konkrete Werte für die<br />
Selektivität angegeben. Außerhalb dieser Spezialfälle, insbesondere bei anderen<br />
Verteilungen als der Gleichverteilung, muß es aber auch möglich sein, zumindest<br />
Abschätzungen für die Selektivität anzugeben. Deshalb werden in diesem Abschnitt<br />
weitere Verfahren vorgestellt, mit denen die Anzahl der Tupel in einem<br />
Zwischenergebnis abgeschätzt werden kann, nämlich:<br />
(a) parametrisierte Verteilungen,<br />
(b) Histogramme <strong>und</strong><br />
(c) Stichproben.<br />
Diese Verfahren sind natürlich auch in den bereits genannten Spezialfällen anwendbar<br />
<strong>und</strong> können die dortigen Abschätzungen verfeinern.<br />
4.4.1 Parametrisierte Verteilung<br />
Bei dieser Methode versucht man, zu der vorhandenen Werteverteilung die Parameter<br />
einer Verteilungsfunktion so zu bestimmen, daß diese die Verteilung gut<br />
annähert.<br />
Dabei kann nicht immer die Normalverteilung die tatsächliche Verteilung annähern.<br />
Aber dafür ist eine Abschätzung der Selektivität sehr einfach zu berechnen<br />
(die Approximationsfunktion liefert die Anzahl der Tupel im qualifizierten Bereich.).<br />
0.4<br />
0.2<br />
0<br />
Abbildung 4.2: Normalverteilter Stichprobenraum<br />
x
54 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
Suboptimal ist dabei nur, daß realistische Verteilungsfunktionen oft nicht gut<br />
mit parametrischen Funktionen angenähert werden können. Vor allem bei mehrdimensionalen<br />
Anfragen (d.h. bei Selektionen, die sich auf mehrere Attribute<br />
beziehen) ist dies sehr schwierig.<br />
Bei den meisten Verfahren wird deshalb auf komplexere Funktionen als die Standard-Normalverteilung<br />
zurückgegriffen. Schließlich muß auch eine sinnvolle <strong>und</strong><br />
effiziente Möglichkeit der Parameterbestimmung gef<strong>und</strong>en werden, dafür leistet<br />
man sich Stichproben, die aber sehr kostenintensiv sein können.<br />
4.4.2 Histogramme<br />
Bei dieser Methode wird der Wertebereich der betreffenden Attribute in Intervalle<br />
unterteilt <strong>und</strong> alle Werte gezählt, die in ein bestimmtes Intervall fallen. Auf diese<br />
Weise ist eine sehr viel flexiblere Annäherung der Verteilung möglich.<br />
Die einfachste Art von Histogrammen unterteilt den Wertebereich in äquidistante<br />
Teilbereiche. Das hat den Nachteil, daß vergleichsweise selten vorkommende<br />
Bereiche zu gut <strong>und</strong> dafür sehr häufig vorkommende zu ungenau abgeschätzt<br />
werden.<br />
Aus diesem Gr<strong>und</strong> werden sogenannte Equi-Depth-Histogramme benutzt, die<br />
den Wertebereich so in Abschnitte unterteilen, daß in jedem Abschnitt gleich<br />
viele Werte liegen. Somit sind Abschnitte mit wenigen Werten sehr grob, stark<br />
frequentierte Bereiche feiner unterteilt. Mit dieser Methode wird eine genauere<br />
Annäherung möglich. Nachteilig ist dabei der höhere Verwaltungsaufwand, da<br />
Equi-Depth-Histogramme nur mit hohen Kosten an Veränderungen der Datenbasis<br />
angepaßt werden können.<br />
0.4<br />
0.2<br />
0<br />
Abbildung 4.3: Equi-Depth-Histogramm<br />
x
4.5. FAZIT DER SELEKTIVITÄTSABSCHÄTZUNGEN 55<br />
4.4.3 Stichproben<br />
Dieses Verfahren ist recht leicht zu realisieren. Es wird einfach eine zufällige<br />
Menge von Tupeln einer Relation gezogen <strong>und</strong> deren Verteilung als repräsentativ<br />
angenommen.<br />
Aber auch diese Methode beinhaltet hohe Kosten im Bereich der Zugriffe. Es<br />
ist somit sehr wichtig, daß nicht mehr Zeit durch das Ziehen der Stichproben<br />
aufgewendet wird, als für eine beliebige Abarbeitung der Anfrage. Daraus folgt,<br />
daß auch dieses Verfahren adaptiv agieren muß.<br />
4.4.4 Zusammenfassung<br />
Schließlich fassen wir nochmal zusammen:<br />
Parametrisierte Histogramme Stichproben<br />
Verteilung<br />
Funktion Werteverteilung diskrete Einteilung, zufällige<br />
über Funktion äquidistant, equi-depth Auswahl<br />
Vorteile einfach sehr genaue einfach<br />
Annäherung<br />
Nachteile ungenau Verwaltungsaufwand Entnahme der<br />
hoch Stichproben teuer<br />
Tabelle 4.2: Zusammenfassung der Verfahren<br />
4.5 Fazit der Selektivitätsabschätzungen<br />
Basierend auf den Selektivitätsabschätzungen kann jetzt die Kardinalität einer<br />
Anfrage P bzw. die Kardinalität des Zwischenergebnisses eines Operatorgraphen<br />
(Anfragebaum) abgeschätzt werden (zur Vereinfachung sollen im folgenden<br />
q(σpj (R)) <strong>und</strong> q(R1 ✶pj R2) mit q(pj) bezeichnet werden).<br />
Es sei folgende SQL-Anfrage P beispielsweise gegeben:
56 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
Select ∗<br />
From R1, R2, . . . , Rn<br />
Where p1 ∧ p2 ∧ . . . ∧ pm<br />
Dabei seien die Ri (i = 1, . . . , n) Relationen <strong>und</strong> die pj (j = 1, . . . , m) entweder<br />
relationenlokale Selektionsprädikate oder relationenübergreifende Joinprädikate.<br />
Die Größe der Ergebnisrelation der Anfrage P erlangt man durch folgende Formel:<br />
|P| =<br />
n�<br />
|Ri| ·<br />
i=1<br />
m�<br />
q(pj)<br />
Diese ergibt sich aus der Überlegung, daß die Anfrage wie folgt bearbeitet werden<br />
könnte. (Selbstverständlich kann dieser Plan nicht der optimale sein, zur<br />
Berechnung der Größe der Ergebnisrelation ist er aber ausreichend.)<br />
Zunächst wird das Kartesische Produkt der n Relationen Ri gebildet. Dieses hat<br />
die Kardinalität �n i=1 |Ri|. Daraufhin werden die Joinbedingungen in Selektionsbedingungen<br />
auf dem Produkt umgewandelt. Es bleiben also nur Selektionen, die<br />
auf das gesamte Produkt angewendet werden müssen. Dadurch wird die Kardinalität<br />
des Kartesischen Produktes um den Faktor �m j=1 q(pj) verringert, da die<br />
Bedingungen pj mit Undzeichen verknüpft sind <strong>und</strong> hierfür die Selektivität induktiv<br />
(siehe Tabelle 4.1) das Produkt der einzelnen Selektivitäten ist. Es ergibt<br />
sich die oben genannte Formel.<br />
Hier noch ein paar kleine Beispiele:<br />
j=1<br />
Ausdruck |Ausdruck|<br />
R1 ✶p R2 |R1| · |R2| · q(R1 ✶p R2)<br />
σ(R1) |R1| · q(σ(R1))<br />
Abbildung 4.4: Beispiel für die Kardinalitätsabschätzung<br />
Im allgemeinen Fall müssen kompliziertere Berechnungen vorgenommen werden,<br />
da sich die Kardinalitäten rekursiv aus den voherigen Knoten ergeben. Leider<br />
kann man dafür keine geschlossene Formel angeben. Damit kann man die Kardinalität<br />
des Ergebnisses eines Anfragebaumes aus den oben bestimmten Abschätzungen<br />
wie folgt berechnen:<br />
Ausgehend von den Blättern des Baumes muß rekursiv in jedem inneren Knoten<br />
die Kardinalität des von diesem Knoten bestimmten Zwischenergebnisses, welches<br />
mit R bezeichnet wird, ermittelt werden. Für die Operation in dem betrachteten
4.6. KOSTENFUNKTION 57<br />
Knoten kann die Selektivität nach den oben entwickelten Formeln abgeschätzt<br />
werden. Die Kardinalität von R ergibt sich daraus <strong>und</strong> aus den Kardinalitäten der<br />
von den Kindern bestimmten Zwischenergebnisse (bzw. der Ausgangsrelationen),<br />
also den Operanden der Operation.<br />
Diese sind zuvor bereits abgeschätzt worden, ebenso wie die für die Selektivitätsabschätzungen<br />
benötigten Zusatzinformationen wie z.B. die Anzahl der verschiedenen<br />
auftretenden Atributwerte (n(·, ·)) oder Schlüsseleigenschaften eines bestimmten<br />
Attributes. Aus dem vorherigen Abschnitt sind ebenfalls Formeln zur<br />
Approximation der Werte n(·, ·) bekannt. Diese werden für die Relation R berechnet,<br />
so daß diese Informationen für den Elternknoten zur Verfügung gestellt<br />
werden können.<br />
4.6 Kostenfunktion<br />
4.6.1 Kostenfunktion Scan (Selektion, Projektion)<br />
Faßt man die Schreibweise der Selektion <strong>und</strong> der Projektion als Scanp(R) zusammen,<br />
muß man bei der Projektion als Bedingung true wählen. Scan-Kosten bzw.<br />
Selektions- <strong>und</strong> Projektionskosten sind für eine Relation R : 2<br />
Scan(R) I/O − Kosten (Scan(R)) CPU − Kosten (Scan(R))<br />
Scan Rel<br />
p (R) page(R) |R|<br />
Scan Index<br />
p (R) page(Index(R)) + min(page(R), q(σp(R))·<br />
(q(σp(R)) · page(R) · |Index(R)|) (|R| + |Index(R)|)<br />
Tabelle 4.3: Scankosten<br />
Die Kostenfunktion für einen relationalen Scan ist leicht nachvollziehbar, wenn<br />
man weiß, daß page(R) die Anzahl von Seiten darstellt, die man für das Abspeichern<br />
der Relation R benötigt. Die dazugehörenden CPU-Kosten bestehen aus<br />
der Kardinalität der Relation R, da jedes Tupel genau einmal betrachtet werden<br />
muß.<br />
Bei einem indexbasierten Scan bestehen die I/O-Kosten aus den benötigten Seitenzugriffen<br />
zum Laden des Index in den Hauptspeicher page(Index(R)). Hin-<br />
2 Mit |Index(R)| wird die Höhe des Indexbaumes bezeichnet.
58 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
zugerechnet werden die Kosten des Ladens der benötigten Tupel. Nun sind zwei<br />
Fälle zu unterscheiden, es könnte entweder günstiger sein, gleich alle Seiten der<br />
Relation R zu laden, oder aber nur diejenigen, in denen sich tatsächlich die qualifizierenden<br />
Tupel befinden. Im ersten Fall ergeben sich die Kosten zu page(R).<br />
Im zweiten Fall hingegen ergibt sich folgende Rechnung. Es qualifizieren sich<br />
q(σp(R))·|R| Tupel, für jedes dieser Tupel entstehen Indexkosten von |Index(R)|.<br />
Auf jeder Seite werden durchschnittlich<br />
|R|<br />
page(R)<br />
Tupel abgespeichert, so daß zum<br />
Laden aller Seiten, die sich qualifizierende Tupel enthalten, Kosten in Höhe von<br />
ca.<br />
q(σp(R)) · |R| · |Index(R)|<br />
|R|<br />
page(R)<br />
= q(σp(R)) · page(R) · |Index(R)|<br />
anfallen. Die CPU-Kosten ergeben sich daraus, daß man alle Tupel, die die Bedingung<br />
p erfüllen, bearbeiten muß, das sind also Kosten von q(σp(R))·|R|. Dazu<br />
werden die Kosten für die Indexzugriffe in Höhe von q(σp(R))·|Index(R)| addiert.<br />
4.6.2 Kostenfunktion Sortierung <strong>und</strong> Indexerzeugung<br />
Bei Merge-Join bzw. Hash-Join fallen unter Umständen zusätzliche Sortierungs-<br />
[Sort(R)] <strong>und</strong> Hash-Operationen [Hash(R)] an, die bei einer Berechnung der Zugriffskosten<br />
mitberücksichtigt werden müssen. Darüberhinaus müssen auch Indexerzeugungen<br />
bewertet werden. Dafür kann man entweder Hash-Funktionen<br />
[Hash(R)] oder den Aufbau eines Indexbaumes [Tree (R)] nutzen. In diesem Abschnitt<br />
werden die Kosten für diese Operationen angegeben. Sort-, Hash- <strong>und</strong><br />
Tree-Kosten einer Relation R ergeben sich wie folgt: 3<br />
I/O-Kosten (·) CPU-Kosten (·)<br />
Sort(R) page(R) · log2(page(R)) |R| · log2(|R|)<br />
Hash(R)<br />
page(R)<br />
k<br />
|R|<br />
Tree(R) page(R) · log2(page(R)) |R| · log2(|R|)<br />
Tabelle 4.4: Sort- Hash- <strong>und</strong> Tree-Kosten<br />
Die Sortierungskosten entsprechen den allgemein bekannten Laufzeiten von Sortieralgorithmen<br />
von n · log(n). Dagegen werden beim Hashing die eingelesenen<br />
3 k entspricht der Buffergröße des vorhandenen Systems. Bei den folgenden Beispielen wird<br />
k mit 55 festgelegt.
4.6. KOSTENFUNKTION 59<br />
Seiten der Relation R in k Buckets gelegt, um danach schnell per Hashfunktion<br />
darauf zugreifen zu können (siehe Kapitel 3.3.4).<br />
4.6.3 Kostenfunktion Join<br />
Die Joinkosten entsprechen den Zugriffsplänen der einzelnen Iteratoren für die<br />
verschiedenen Joinoperationen. Beim NestedLoop-Join wird nach dem einmaligen<br />
Laden der ersten Relation R1, so oft R2 eingelesen, wie R1 Tupel besitzt.<br />
Join-Kosten für Relationen R1 ✶ R2: 4<br />
R1 ✶p R2 I/O − Kosten (·) CPU − Kosten (·)<br />
NestedLoop-Join 4 Scan Rel<br />
p (R1)+ |R1| + |R1| · |R2|<br />
|R1|·Scan Rel<br />
p (R2)<br />
Merge-Join Scan Rel<br />
p (R1)+ q(R1 ✶p R2)·<br />
(R1 & R2 sortiert) Scan Rel<br />
p (R2) (|R1| + |R2|)<br />
Hash-Join Scan Rel<br />
p (R1)+ q(R1 ✶p R2)·<br />
(R1 & R2 gehashed) Scan Rel<br />
p (R2) (|R1| · |R2|)<br />
Index-Join 4 Scan Rel<br />
p (R1)+ q(R1 ✶p R2)·<br />
(Index auf R2) Scan Index<br />
p (R2) (|R1| + |R2|)<br />
Index-Index-Join 4 Scan Index<br />
p (R1)+ q(R1 ✶p R2)·<br />
(Index auf R1, R2) Scan Index<br />
p (R2) (|R1| + |R2|)<br />
Tabelle 4.5: Join-Kosten<br />
4 Da die Kostenfunktionen in der Literatur nur teilweise zu finden sind, wurde an gegebener<br />
Stelle eine vom Autor selbst entwickelte Kostenfunktion hinzugefügt.
60 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
Beim Merge-Join <strong>und</strong> den Varianten des Index-Joins sind die CPU-Kosten gleich,<br />
da sich hier die gesamten qualifizierten Tupel wiederfinden. Die I/O-Kosten entsprechen<br />
wieder den Zugriffsmethoden der einzelnen Iteratoren. Beim Index-Join<br />
wird zum Beispiel der Zugriff auf die Relation R1 mit einem Index-Scan durchgeführt.<br />
Nicht zu vergessen ist der Hash-Join, dessen Hash-Kosten für die einzelnen Relationen<br />
R1, R2 noch zu den Standardkosten (I/O-Kosten bzw. CPU-Kosten) addiert<br />
werden müssen.<br />
4.7 Kosteneinsparungen beim Pipelining<br />
Da beim Pipelining keine Zwischenspeicherung der einzelnen Tupel stattfindet,<br />
fallen einige der I/O-Kosten weg. Betrachtet man das Pipelining 〈O1◦ 1 · · ·◦ l−1 Ol〉<br />
der Operationen O1, . . . , Ol, wobei ◦ i jeweils für eines der Symbole ◦, ◦1, ◦2 stehe,<br />
so erhält man die Kosten des Pipelinings wie folgt:<br />
Die Operation Ol, also die zuerst auszuführende Operation, hat die gleichen Kosten<br />
wie in einem Plan ohne Pipelining. Sei nun Oi eine der anderen Operation,<br />
also 1 ≤ i ≤ l − 1. Nun sind drei Fälle zu unterscheiden,<br />
(a) Oi ist eine einstellige Operation, arbeitet also auf einer Relation, dann ist<br />
◦ i = ◦,<br />
(b) Oi ist eine zweistellige Operation, arbeitet also auf zwei Relationen, <strong>und</strong><br />
das Pipelining ist auf dem ersten Operanden, dann ist ◦ i = ◦1 oder<br />
(c) Oi ist eine zweistellige Operation, arbeitet also auf zwei Relationen, <strong>und</strong><br />
das Pipelining ist auf dem zweiten Operanden, dann ist ◦ i = ◦2.<br />
Im ersten Fall ergibt sich, daß für die Operation Oi keine I/O-Kosten anfallen,<br />
da die gesamten Tupel direkt weiterverarbeitet werden. Im zweiten Fall werden<br />
die I/O-Kosten um den Anteil der ersten Relation reduziert, d.h. die I/O-Kosten<br />
entsprechen denen, die für den Zugriff auf die zweite Relation anfallen.<br />
Die Kosten beim dritten Fall ergeben sich analog, nur daß nun die Zugriffskosten<br />
für die zweite Relation wegfallen. Dabei sei daran erinnert, daß bei unserem<br />
Kostenmodell die I/O-Kosten nur die Input-Kosten beinhalten.<br />
4.8 Beispiel zur Kostenberechnung<br />
Zum Abschluß des Kapitels sollen noch ein paar einfache Beispiele den Ablauf<br />
der Kostenberechnung verdeutlichen:
4.8. BEISPIEL ZUR KOSTENBERECHNUNG 61<br />
Seien folgende Werte für eine Relation R gegeben (siehe Abbildung 4.6).<br />
Metadaten Wert<br />
page|R| 1000<br />
|R| 10000<br />
n(A, R) 50<br />
|Index(R)| 4<br />
page(Index(R)) 50<br />
Tabelle 4.6: Metadaten für die Relation R<br />
Zusätzlich dazu soll die statistische Annahme bezüglich der Gleichverteilung der<br />
Werte über den Wertebereich eines Attributs <strong>und</strong> der Unabhängigkeit zwischen<br />
den Werten verschiedener Attribute vorausgesetzt werden.<br />
Desweiteren wird ein System eingesetzt, welches I/O-Constraint ist <strong>und</strong> somit<br />
den Wert W = 0, 02 besitzt:<br />
1) Bei der ersten Variante betrachten wir einen einfachen Relationen Scan:<br />
σ Rel<br />
p (R) mit p = Aθa. Hierbei gehen wir davon aus, daß alle Seiten der Datenbank<br />
gelesen <strong>und</strong> jeweils alle auf den Seiten vorhandenen Tupel gef<strong>und</strong>en<br />
werden müssen (siehe dazu 4.6.1):<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
page(R) |R| page(R) + 0, 02 · |R|<br />
1000 10000 (1000 + 0, 02 · 10000) = 1200<br />
2) Bei der zweiten Variante existiert ein Index auf R über dem Selektionsattribut<br />
p: σIndex p (R) mit p = Aθa. Wir können daher den Zugriff beschleunigen<br />
(siehe dazu 4.6.1):<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
page(Index(R)) + min(page(R), q(σp(R))· I/O + 0, 02· CPU<br />
(q(σp(R)) · page(R) · |Index(R)|)) (|R| + |Index(R)|)<br />
50 + min(1000, 1<br />
1<br />
50 · 1000 · 4) 50 · (10000 + 4) (130 + 0, 02 · 200) = 134<br />
= 130 ≈ 200
62 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
Die Analyse der Zahlen zeigt das zu erwartende Ergebnis, nämlich daß für die<br />
vorgegebene Relation R ein Indexzugriff immer schneller ist, als ein einfacher<br />
Relationen-Scan.<br />
Nach dieser einfachen Berechnung einer Kostenabschätzung, kommen wir jetzt<br />
auf unser Standardbeispiel aus Kapitel 2.2 zurück. Wiederum nehmen wir für W<br />
einen Wert von 0, 02 an.<br />
4. Level ( ˆ R)<br />
3. Level ( ˜ R)<br />
2. Level (R)<br />
1. Level ( ˜ R1) ✲<br />
✲<br />
✲<br />
✲<br />
π Rel<br />
B,D<br />
σ Rel<br />
E=2<br />
✶ Rel,Index<br />
R1.C=R2.C<br />
✪ ❡<br />
✪ ❡<br />
✪<br />
✪<br />
σ Index<br />
A=c<br />
Abbildung 4.5: physisch optimierter Anfragebaum<br />
Dabei benutzen wir andere Ausgangsrelationen als zuvor, da die bisherigen zu<br />
klein sind <strong>und</strong> deswegen keinen sinnvollen Anwendungsbereich für unsere Abschätzungen<br />
darstellen. Zur Vereinfachung werden folgende Abkürzungen definiert:<br />
�R1 := σA=c(R1)<br />
R1<br />
R2<br />
R := ✶ Rel,Index<br />
�R1.C=R2.C ( � R1, R2)<br />
�R := σ Rel<br />
E=2(R)<br />
�R := π Rel<br />
B,D( � R)<br />
Für R1, R2 seien folgende Metadaten bekannt. Für R1 sei das Attribut A der<br />
Schlüssel der Relation <strong>und</strong> für R2 sei das Attribut C der Schlüssel der Relation<br />
(siehe Abbildung 4.7).<br />
1.Level In diesem Level muß � R1 = σ Index<br />
A=c (R1) berechnet werden (siehe dazu Kapitel<br />
4.6.1).<br />
Dabei wird zur Berechnung die Selektivität von σp(R1) benötigt, die man -<br />
wie im Kapitel 4.3.1 erwähnt - aus der Kardinalität des Wertebereichs des<br />
Attributes A bekommt:<br />
q(σA=c(R1)) = | σA=c(R1) |<br />
| R1 |<br />
=<br />
1<br />
n(A, R1)<br />
⇔ | � R1| = |σA=c(R1)| = q(σA=c(R1)) · |R1| = |R1|<br />
n(A, R1)<br />
10000<br />
= = 1<br />
10000
4.8. BEISPIEL ZUR KOSTENBERECHNUNG 63<br />
Metadaten Wert<br />
page|R1| 1000<br />
|R1| 10000<br />
n(A, R1) 10000<br />
n(B, R1) 10<br />
n(C, R1) 100<br />
|IndexA(R1)| 4<br />
page(IndexA(R1)) 50<br />
Metadaten Wert<br />
page|R2| 1000<br />
|R2| 10000<br />
n(C, R2) 10000<br />
n(D, R2) 10<br />
n(E, R2) 100<br />
|IndexC(R2)| 4<br />
page(IndexC(R2)) 50<br />
Tabelle 4.7: Metadaten für die Relationen R1 <strong>und</strong> R2<br />
Die Kosten für die Selektion ergeben sich damit zu:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
page(IndexA(R1)) + min(page(R1), q(σA=c(R1)))· I/O + 0, 02· CPU<br />
(q(σA=c(R1)) · page(R1) · |IndexA(R1)|) (|R1| + |IndexA(R1)|)<br />
1<br />
1<br />
50 + min(1000, 10000 · 1000 · 4)<br />
10000 · (10000 + 4) (50 + 0, 02 · 1) ≈ 50<br />
≈ 50 ≈ 1<br />
2.Level Jetzt werden die Kosten für die Berechnung von R =✶ Rel,Index<br />
�R1.C=R2.C ( � R1, R2)<br />
benötigt (siehe Kapitel 4.6.3).<br />
Dazu wird zunächst Scan Index<br />
C=�R1.C (R2) berechnet:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
page(IndexC(R2)) + min(page(R2), q(σ C=�R1.C (R2)))· I/O + 0, 02· CPU<br />
(q(σ C=�R1.C (R2)) · page(R2) · |IndexC(R2)|) (|R2| + |IndexC(R2)|)<br />
1<br />
1<br />
50 + min(1000, 10000 · 1000 · 4)<br />
10000 · (10000 + 4) (50 + 0, 02 · 1) ≈ 50<br />
≈ 50 ≈ 1<br />
Schließlich wird der erste Teil der I/O-Kosten für die Berechnung der Joinkosten<br />
auf Null gesetzt, da ein Pipelining im ersten Operanden stattfindet.<br />
Desweiteren wird die Selektivität von ✶ Rel,Index<br />
�R1.C=R2.C ( � R1, R2) benötigt, die man<br />
- wie im Kapitel 4.3.2 erläutert - mit 1<br />
|R2|<br />
abschätzen kann:
64 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
0 + Scan Index (R2) q( � R1 ✶�R1.C=R2.C R2) · (| � R1| + |R2|) I/O + 0, 02· CPU<br />
0 + 50 = 50<br />
1<br />
10000 · (1 + 10000) ≈ 1 (50 + 0, 02 · 1) ≈ 50<br />
3.Level In diesem Schritt benötigen wir die Kosten für � R = σRel E=2 (R), die als Eingaberelation<br />
das vorhergehende Join hat. Dafür benötigen wir die dazugehörige<br />
Kardinalität (siehe dazu Kapitel 4.5):<br />
|R| = | � R1| = 1,<br />
da das Attribut C Schlüssel der Relation R2 ist.<br />
Somit ergibt sich für � R = σRel E=2 (R) wegen des Pipelinings:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
0 |R| I/O + 0, 02· CPU<br />
0 1 0 + 0, 02 · 1 ≈ 0<br />
4.Level Am letzten Knoten müssen noch die Kosten für die geforderte Projektion<br />
�R = π Rel<br />
B,D ( � R) berechnet werden. Als Eingabe bekommt sie das Ergebnis der<br />
vorhergehende Selektion: � R = σ Rel<br />
E=2 (R).<br />
Wiederum wird die Kardinalität benötigt:<br />
|σ Rel<br />
E=2(R)| = q(σE=2(R)) · |R|<br />
⇒ |σ Rel<br />
E=2(R)| ≤<br />
1<br />
· 1<br />
n(E, R)<br />
⇒ |σ Rel<br />
E=2(R)| ≤ 1<br />
· 1 = 1<br />
1<br />
Damit ergibt sich unter Berücksichtigung des Pipelinings:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
0 | � R| I/O + 0, 02· CPU<br />
0 1 (0 + 0, 02 · 1) ≈ 0<br />
Somit ergibt sich folgender Kostenwert für den oben genannten Anfrageplan:<br />
cost(P) = 50 + 50 + 0 + 0 = 100
4.9. FAZIT DES KOSTENMODELLS 65<br />
4.9 Fazit des Kostenmodells<br />
Abschließend muß man noch sagen, daß es bei einem sinnvollen Kostenmodell<br />
nicht auf die exakte Abschätzung der Kosten ankommt, sondern darauf, daß<br />
sich die realen Kostendifferenzen der einzelnen Ausführungspläne für die gleiche<br />
Anfrage in dem Modell wiederfinden. Das heißt, daß kleine Fehler <strong>und</strong> Ungenauigkeiten<br />
in den Abschätzungen keine Auswirkung auf die Bewertungsreihenfolge<br />
der untersuchten Pläne haben. Die relative Bewertung der Pläne <strong>und</strong> nicht die<br />
Bestimmung der genauen Kosten oder Antwortzeiten ist ausschlaggebend. Diese<br />
Anforderung an ein gutes Kostenmodell läßt sich leicht nachprüfen, indem man<br />
probehalber eine Anfrage Brute-Force optimiert, sich dann die Kostendifferenzen<br />
anschaut <strong>und</strong> mit den geschätzten Differenzen vergleicht. Entscheidend ist,<br />
daß der geschätzte beste Plan auch tatsächlich in die Nähe des Kostenminimums<br />
gelangt.<br />
Heute erlangt dieses Thema eine immer wichtigere Rolle <strong>und</strong> wird unter dem<br />
Namen ” statistical database profile“ geführt. Dabei wird das statistische Profil<br />
einer Datenbank durch Verwendung von Statistiken <strong>und</strong> statistischen Analysen<br />
summiert, das dann als Gr<strong>und</strong>lage für die Anfrageoptimierung, den physischen<br />
Datenbank-Entwurf <strong>und</strong> die Leistungsvorhersagen einer Datenbank benutzt wird.
66 KAPITEL 4. KOSTENFUNKTIONEN UND SELEKTIVITÄTEN
Kapitel 5<br />
Manchmal bezahlt man den höchsten Preis<br />
für Dinge, die man umsonst erhält.<br />
Albert Einstein<br />
Kostenbasierte Optimierung<br />
Nach erfolgreicher Entwicklung der physischen Prototypen (siehe Abschnitt 3.8)<br />
wird mit Hilfe der im letzten Kapitel vorgestellten Kostenfunktion die Möglichkeit<br />
geschaffen, daß ein Suchalgorithmus ausgehend von dem Prototypen den besten<br />
Anfragebaum finden kann.<br />
Die Ausgangssituation ist folgende: Die gegebene Anfrage wird in einen Anfragebaum<br />
übersetzt <strong>und</strong> dieser zu einem oder mehreren Prototypen umgeformt 1 .<br />
Dieser Prototyp entsteht durch Ersetzung der algebraischen Operatoren durch<br />
physische (siehe Abschnitt 2.3 <strong>und</strong> 3.8). Mittels der Ersetzungsregeln der physischen<br />
Algebra können daraufhin diese Operatorbäume weiter modifiziert werden.<br />
1:1-Übersetzung<br />
❚<br />
❚<br />
❚<br />
❚�<br />
✻<br />
✲<br />
��✠ Anfrageraum<br />
Kostenfunktion<br />
��✠<br />
�<br />
❅❄<br />
❅<br />
❅ Prototyp<br />
Abbildung 5.1: Suchraum der Ausführungspläne<br />
1 In der später vorgestellten Implementierung (siehe Kapitel 7) wird durch die beiden Heuristiken<br />
(siehe Abschnitte 2.3 <strong>und</strong> 3.8) jeweils nur ein Prototyp erzeugt.<br />
67
68 KAPITEL 5. KOSTENBASIERTE OPTIMIERUNG<br />
5.1 Heuristische Suchverfahren<br />
In diesem Abschnitt sollen verschiedene heuristische Suchverfahren vorgestellt<br />
werden, um dann die besten davon für die kostenbasierte Optimierung einzusetzen.<br />
Allen diesen Suchverfahren liegt das Prinzip zugr<strong>und</strong>e, daß ausgehend von<br />
einem Startknoten sukzessive Nachbarn besucht <strong>und</strong> bewertet werden. Sie unterscheiden<br />
sich in der Reihenfolge der Abarbeitung der Nachbarn <strong>und</strong> in der Frage,<br />
ob ein Suchbaum aufgebaut wird oder nicht.<br />
Um also ein solches Verfahren anwenden zu können, muß erstens ein Startknoten<br />
erzeugt werden, dieses ist die Prototypentwicklung. Zweitens muß eine Methode<br />
bereitstehen, die zu einem gegebenen Knoten die Nachbarn ausgibt. Im Fall der<br />
kostenbasierten Anfrageoptimierung sollen hier die Nachbarn eines Operatorbaumes<br />
alle Operatorbäume sein, die aus dem gegebenen Baum durch Anwendung<br />
einer Ersetzungsregel entstehen. Dabei kann es bei der Implementierung sinnvoll<br />
sein, Ersetzungsregeln, die die Laufzeit der Anfragebearbeitung bekanntermaßen<br />
verlängern, von vornherein auszuschließen. Drittens muß eine Bewertungsfunktion<br />
zur Verfügung stehen, mit der einem Knoten ein Wert zugeordnet wird, der<br />
die Qualität des Knotens widerspiegelt. Dabei ist es für die Funktionsweise der<br />
Suchverfahren zunächst einmal unbedeutend, ob diese Funktion eine Art Gütefunktion<br />
ist, dementsprechend ein Maximum dieser Funktion gesucht wird, oder<br />
ob sie eher eine Kostenfunktion darstellt <strong>und</strong> von daher ein Minimum zu finden<br />
ist. Zur Vereinfachung der Darstellung soll angenommen werden, daß ein Knoten<br />
gesucht wird, der ein (absolutes) Minimum der Bewertungsfunktion realisiert.<br />
Dieser paßt dann mit der kostenbasierten Optimierung durch die Kostenfunktion<br />
zusammen.<br />
5.1.1 Hill-Climbing Suche<br />
Hill-Climbing ist ein heuristisches Suchverfahren, das ohne einen Suchbaum auskommt.<br />
Der Algorithmus speichert den Startknoten als BisherigerKnoten <strong>und</strong><br />
iteriert folgendes Verfahren:<br />
(a) Bestimme alle Nachbarn von BisherigerKnoten <strong>und</strong> bewerte sie.<br />
(b) Speichere den Nachbarknoten mit der niedrigsten Bewertung in Nächster-<br />
Knoten.<br />
(c) Falls die Bewertung von NächsterKnoten besser ist als die von Bisheriger-<br />
Knoten, überschreibe BisherigerKnoten mit dem neu gef<strong>und</strong>enen Knoten<br />
<strong>und</strong> beginne von vorn.<br />
Für den Fall, daß die Bewertung von NächsterKnoten nicht besser ist als die<br />
von dem bisherigen, was ja bedeutet, daß keiner der Nachbarn besser ist als der
5.1. HEURISTISCHE SUCHVERFAHREN 69<br />
bisherige Knoten, bricht der Algorithmus ab <strong>und</strong> gibt BisherigerKnoten aus.<br />
Dieser Algorithmus hat natürlich seine Nachteile, die wichtigsten sind:<br />
(a) Lokales Minimum: Da der Algorithmus abbricht, wenn die Bewertungsfunktion<br />
in der Umgebung des momentanen Knotens keine kleineren Werte mehr<br />
annimmt, kann es sein, daß das Ergebnis ” nur“ ein lokales Minimum ist.<br />
(b) Plateau: Da der Algorithmus nur eine kleine Umgebung untersucht, führen<br />
auch Plateaus zu einem Abbruch. Wenn man bei einem Knoten angekommen<br />
ist, der auf einem Plateau der Kostenfunktion liegt, gibt es in der<br />
kleinen Umgebung um den bisherigen Knoten nur Knoten mit derselben<br />
Bewertung.<br />
Der Algorithmus wird an dieser Stelle abbrechen, da ein Nachbar gesucht<br />
wird, der eine bessere (also echt bessere) Bewertung hat. (Wenn man den<br />
Algorithmus leicht verändert, kann man natürlich auch erreichen, daß er<br />
auf einem Plateau per Zufall einen der gleichwertigen Nachbarn auswählt.<br />
Dies scheint aber nicht sehr erfolgversprechend.)<br />
(c) Sattelpunkt: Auch an einem Sattelpunkt der Kostenfunktion kann der Algorithmus<br />
Probleme bereiten. Da die Nachbarn-Methode nur endlich viele<br />
Nachbarn ausgeben kann, kann ein Sattelpunkt, von dem aus nur in einer<br />
der Hauptrichtungen ein schwacher Abstieg <strong>und</strong> in den anderen ein starker<br />
Anstieg der Bewertungsfunktion vorliegt, zu den gleichen Problemen wie<br />
ein lokales Minimum führen.<br />
Liegt nämlich keiner der Nachbarn hinreichend genau in Richtung des Abstiegs,<br />
wird keiner der Nachbarn eine bessere Bewertung als der momentane<br />
Knoten bekommen. Je kleiner die Anzahl der betrachteten Nachbarn ist,<br />
desto eher stellt ein Sattelpunkt ein Problem dar.<br />
Diese Probleme kann man z.B. dadurch lösen, daß man den Algorithmus mehrfach<br />
mit verschiedenen Startknoten startet. Die Möglichkeit der zufälligen Erzeugung<br />
von Startknoten erscheint nicht praktikabel; durch die Entwicklung mehrerer Prototypen<br />
<strong>und</strong> die Anwendung des Hill-Climbing-Algorithmus auf jeden einzelnen<br />
kann man aber diese Probleme entschärfen.<br />
5.1.2 Simulated Annealing<br />
Eine andere Möglichkeit die genannten Probleme der Hill-Climbing Suche zu<br />
lösen, ist die Idee des Simulated Annealing. Dieser Algorithmus verhält sich ähnlich<br />
wie der Hill-Climbing Algorithmus. Ausgehend von einem Knoten wird aber<br />
nicht aus allen Nachbarn der beste weiterverfolgt, sondern ein zufälliger ausgewählt<br />
<strong>und</strong> bewertet. Abhängig von der Differenz seiner Bewertung <strong>und</strong> der des
70 KAPITEL 5. KOSTENBASIERTE OPTIMIERUNG<br />
vorherigen Knotens bekommt der neue Knoten eine Wahrscheinlichkeit zugeordnet,<br />
mit der er weiterverfolgt wird.<br />
Ist die Differenz positiv, der neue Knoten also besser als der alte, ist die Wahrscheinlichkeit<br />
1, dieser Knoten wird dann weiterverfolgt. Andernfalls stellt der<br />
neue Knoten eine Verschlechterung dar. Je größer die Verschlechterung ist, desto<br />
unwahrscheinlicher wird die Weiterverfolgung dieses Knotens. Darüberhinaus<br />
wird diese Wahrscheinlichkeit mit wachsender Anzahl der durchgeführten Iterationen<br />
reduziert. Das bedeutet, daß sich dieser Algorithmus mit zunehmender<br />
Anzahl von Iterationen immer mehr wie Hill-Climbing verhält, zu Beginn aber<br />
die Möglichkeit hat, ein lokales Minimum oder ähnliches wieder zu verlassen.<br />
5.1.3 Best-First Suche<br />
Mit der Best-First Suche soll nun ein Suchverfahren vorgestellt werden, das mit<br />
einem Suchbaum arbeitet. Das bedeutet natürlich zusätzlichen Aufwand, birgt<br />
aber auch einige Verbesserungsmöglichkeiten gegenüber den Verfahren, die ohne<br />
einen Suchbaum agieren.<br />
Das gr<strong>und</strong>sätzliche Prinzip bei der Suche mit Suchbaum ist das folgende: Die<br />
Wurzel des Suchbaumes ist der gegebene Startknoten (deshalb auch die Sprachregelung<br />
” Knoten“). Die Kinder eines Knotens im Suchbaum sind alle Nachbarn<br />
des Knotens. In diesem Baum muß nun systematisch derjenige Knoten gesucht<br />
werden, der die global beste Bewertung hat. Zum Beispiel könnte man eine Tiefenoder<br />
Breitensuche durchführen, allerdings wird dabei die zusätzliche Information<br />
durch die Kostenfunktion weitgehend vernachlässigt. Eine Möglichkeit, diese<br />
Information mit einzubeziehen, ist die Best-First Suche.<br />
Dieses Suchverfahren gleicht der Tiefensuche, mit dem Unterschied, daß von einem<br />
Knoten aus nicht zunächst der linkeste Nachfolgeknoten besucht wird, sondern<br />
der Knoten betrachtet wird, der die beste Beurteilung erhalten hat. Es<br />
gibt dabei aber natürlich keine Garantie, daß dieser scheinbar beste Pfad auch<br />
tatsächlich zum kostengünstigsten Knoten führt.<br />
Der Algorithmus sieht wie folgt aus:<br />
(a) Erzeuge einen Baum mit dem gegebenen Startknoten als Wurzel.<br />
(b) Bestimme alle Nachbarn dieses Knotens, bewerte sie <strong>und</strong> füge diejenigen,<br />
die noch nicht im Baum stehen, als Kinder des betrachteten Knotens ein.<br />
(c) Wähle den Nachfolgerknoten mit der besten Bewertung aus <strong>und</strong> beginne<br />
wieder bei (b).<br />
(d) Wird ein Blatt des Baumes erreicht, muß entschieden werden, ob dieser<br />
Knoten der Endknoten ist.
5.2. FAZIT DER HEURISTISCHEN SUCHE 71<br />
5.1.4 Beam-Search<br />
Hierbei handelt es sich um ein ähnliches Verfahren wie die Breitensuche. Jedoch<br />
werden pro Ebene nur die x besten Pfade weiter betrachtet. Der Suchbaum bleibt<br />
somit unabhängig vom Branching-Faktor nur x Pfade breit <strong>und</strong> wächst nicht<br />
exponentiell.<br />
Auch dieser Algorithmus kann im schlimmsten Fall keine Lösung liefern, obwohl<br />
eine existiert. Dies ist dann der Fall, wenn die scheinbar x besten Pfade einer<br />
Ebene in weiterer Folge nicht zum Ziel führen.<br />
5.2 Fazit der heuristischen Suche<br />
Bei all diesen Verfahren ist die Vorgabe eines guten Prototyps unvermeidlich, da<br />
man sonst das globale Minimum nicht finden kann. Somit wird der Startort im<br />
Suchraum fest vorgegeben. Er muß in der Nähe des globalen Minimums liegen<br />
<strong>und</strong> weit genug entfernt von vermeintlich sinnvollen lokalen Minima.<br />
Eine weitaus effektivere Suche ließe sich realisieren, wenn man das Ziel kennen<br />
würde; bzw. wissen könnte, in welchem Bereich des Anfrageraumes es liegen wird.<br />
Dadurch könnte eine sinnvollere Einschränkung <strong>und</strong> daraus resultierende Schwellwerte<br />
realisiert werden. Da genau das unser Problem bei der Anfrageoptimierung<br />
ist (der beste Anfragplan ist nicht bekannt), können wir nicht auf Bestinformierte<br />
Heuristiken wie den A*-Algorithmus zurückgreifen.<br />
Somit bleiben uns zur Optimierung der bevorzugten Heuristiken nur sinnvolle<br />
Kombinationen, die den jeweiligen Nachteil der einzelnen Heuristik wieder ausgleichen.<br />
Dazu gehört das parallele Starten verschiedener Optimierungsalgorithmen<br />
mit unterschiedlichen Ausgangspunkten (verschiedene Prototypen), wie auch<br />
der Versuch eines frühzeitigen Prunings (Begrenzung des Lösungsraumes durch<br />
Löschen von schlechten Plänen für die äquivalente Pläne existieren).<br />
5.3 Genetische Algorithmen<br />
Die kanonischen Genetischen Anfrageoptimierungsalgorithmen stellen bei komplexeren<br />
Anfragen alternative Methoden zu deterministischen Verfahren dar, da<br />
sie die Bestimmung des optimalen Ausführungsplanes ohne ein vollständiges<br />
Durchsuchen (Brute-Force-Suche) des Lösungsraumes ermöglichen.<br />
Dabei werden die möglichen Lösungen des Optimierungsproblems als Population<br />
von Individuen in einer künstlichen Welt betrachtet. Der Grad der Anpassung
72 KAPITEL 5. KOSTENBASIERTE OPTIMIERUNG<br />
eines Individuums wird als seine Fitness beschrieben. Die Koordinaten eines Individuums<br />
werden als Gene bezeichnet. Durch Modellierung der evolutionären<br />
Operationen, Rekombinationen, Mutationen <strong>und</strong> Selektionen werden neue Nachfahren<br />
von Suchpunkten gef<strong>und</strong>en, die mit höherer Wahrscheinlichkeit eine bessere<br />
Fitness aufweisen als Ihre Ahnen.<br />
Der Algorithmus sieht wie folgt aus:<br />
• Initialisiere zufällig die Elterngeneration P (t) zum Zeitpunkt t = 0 <strong>und</strong><br />
berechne die gegenwärtige Fitness eines jeden Individuums<br />
• Wiederhole solange bis Abbruch<br />
– Durch Rekombination vermische den Informationsinhalt der elterlichen<br />
Gene miteinander.<br />
– Durch Mutation verändere stochastisch die Gene, so daß neue Individuen<br />
erzeugt werden, die neuartige (innovativere) Geninformationen<br />
besitzen.<br />
– Durch Selektion bevorzuge Individuen mit besserer Fitness für nachkommende<br />
Generationen.<br />
– Berechne die Fitness der gegenwärtigen Population <strong>und</strong> erhöhe das<br />
Zeitalter um eins (t = t + 1).<br />
– Starte die Generierung der neuen Kinderpopulation<br />
Eine Steigerung der Robustheit von Genetischen Algorithmen als Optimierungsstrategie<br />
läßt sich durch eine Parameteranpassung realisieren. Dabei wird die<br />
genetische Suche durch Manipulation der Parameter Populationsgröße, Generationszahl<br />
sowie Mutationsrate an die Dynamik der Optimierung angepaßt.
Kapitel 6<br />
Eine wirklich gute Idee erkennt man daran,<br />
daß ihre Verwirklichung von vorneherein<br />
ausgeschlossen erscheint.<br />
Albert Einstein<br />
Die Optimierungsstrategie<br />
Dieses Kapitel dient der Darstellung <strong>und</strong> Bewertung der entwickelten <strong>und</strong> favorisierten<br />
Optimierungsstrategie. Ausgehend von den Teilstrategien der bisherigen<br />
Kapitel soll verdeutlicht werden, wie die einzelnen Regeln <strong>und</strong> Verfahren zusammenwirken.<br />
Der Ablauf sieht dabei schematisch wie in Abbildung 6.1 dargestellt<br />
aus.<br />
Eingabe: Zu Beginn steht bekanntermaßen die Anfrage des Anwenders, die in<br />
SQL oder einem Ausdruck der Relationenalgebra gestellt wird. Diese Anfrage<br />
muß zunächst in eine adäquate Form gebracht werden. Hierzu wird<br />
die Eingabe mittels eines Parsers in einen äquivalenten algebraischen Anfragebaum<br />
umgewandelt. Dabei werden die eingegeben Operatoren in die<br />
implementierte Datenstruktur übersetzt, aber noch keine Optimierungen<br />
durchgeführt. Dieser Schritt wurde im wesentlichen in der Einleitung beschrieben,<br />
der entstehende Baum ist die sogenannte 1:1-Übersetzung der<br />
Anfrage, dieser wird nun weitergegeben.<br />
Algebraische Optimierung: Bei der algebraischen Optimierung werden, wie<br />
in Kapitel 2.1 genauer dargestellt, ausgehend von der 1:1-Übersetzung algebraische<br />
Prototypen erstellt. Gr<strong>und</strong>lage hierfür sind die Faustregeln aus Abschnitt<br />
2.3. Da diese teilweise miteinander konkurrieren, erhält man durch<br />
verschiedene Reihenfolgen der Anwendung der Faustregeln im allgemeinen<br />
verschiedene Prototypen. Dies ist durchaus so gewollt, da durch die Verwendung<br />
mehrerer Startwerte mit genügend großem Abstand die Probleme<br />
der heuristischen Suchverfahren relativiert werden.<br />
Allerdings ist der Zusatz ” mit genügend großem Abstand“ sehr entscheidend.<br />
Startet man nämlich z. B. mit zwei sehr ähnlichen Startwerten, erhöht<br />
sich die Laufzeit der Suche, aber es wird kein Vorteil erreicht, da in beiden<br />
Suchdurchläufen ohnehin dieselbe Region untersucht wird. Deshalb ist<br />
73
74 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
Anfrage (SQL)<br />
Normalisierung<br />
❄<br />
Anfragebaum<br />
alg. Transformation<br />
❄<br />
Prototypen (alg.)<br />
phys. Transformation<br />
❄<br />
Prototypen (physisch)<br />
Hill-Climbing<br />
❄<br />
Eingabe<br />
Algebraische Optimierung<br />
Physische Optimierung<br />
mehrere Vorschläge Kostenbasierte Optimierung<br />
Minimum<br />
❄<br />
opt. Operatorbaum<br />
Ausführung<br />
❄<br />
Ergebnisrelation<br />
Ausgabe<br />
Abbildung 6.1: Ablauf der Anfrageoptimierung<br />
es wichtig, die erzeugten Prototypen vor der Weitergabe dahingehend zu<br />
untersuchen, daß der paarweise Abstand groß genug ist. Der Abstand ergibt<br />
sich hierbei durch die Anzahl der benötigten Transformationsschritte<br />
bei der Umformung des ersten Baumes in den zweiten. Für diesen Teil der<br />
Strategie fehlen aber leider noch die Erfahrungswerte dafür, was es heißt<br />
genügend weit von einander entfernt zu sein. Aus diesem Gr<strong>und</strong> wird bisher<br />
nur ein Prototyp erstellt. 1<br />
Dieser Teil der Optimierungsstrategie besteht, wie der Name schon sagt,<br />
ausschließlich aus algebraischen Optimierungen. Am Ende werden im allgemeinen<br />
mehrere algebraische Prototypen weitergegeben.<br />
1 Bei einem Prototyp läuft man nicht Gefahr, daß die Abstände zwischen den einzelnen<br />
Alternativen zu klein sind. Somit erfüllt ein Prototyp trivialerweise die Forderung hinreichend<br />
unterschiedlich zu sein.
Physische Optimierung: Im nächsten Schritt müssen die algebraischen Operatoren<br />
eines jeden übergebenen algebraischen Prototypen in physische<br />
Operatoren übersetzt werden. Die theoretische Gr<strong>und</strong>lage hierfür liefert<br />
Kapitel 3.<br />
Hierbei ist es analog zur obigen Überlegung weder notwendig noch sinnvoll,<br />
jeden algebraischen Operatorbaum in genau einen physischen Anfragebaum<br />
zu übersetzen. Mehr noch, durch die Erzeugung mehrerer Übersetzungen<br />
werden wiederum mehr Startwerte für die Suchverfahren zur Verfügung<br />
gestellt. Hier muß aber eine sinnvolle Abwägung durchgeführt werden. Denn<br />
die ” einfache“ Methode, jede erdenkliche Übersetzung in Betracht zu ziehen,<br />
führt auf eine zu große Anzahl von Startwerten, die auch nicht hilfreich ist,<br />
da die Abstände hierbei eindeutig zu klein werden.<br />
Dementsprechend werden die Regeln aus Abschnitt 3.8 angewandt, um<br />
verschiedene Varianten jedes algebraischen Prototypen zu erhalten. Dabei<br />
genügt es bei der Übersetzung darauf zu achten, daß zwei Übersetzungen<br />
eines algebraischen Baumes hinreichend unterschiedlich sind. Dies ist der<br />
Fall, da die algebraischen Prototypen bereits paarweise genügend unterschiedlich<br />
übergeben werden, <strong>und</strong> bei der Übersetzung keine strukturellen<br />
Änderungen am Baum durchgeführt werden. Dementsprechend kann die<br />
Distanz der resultierenden physischen Bäume nicht kleiner werden als der<br />
Abstand der algebraischen war, so daß die Übersetzungen verschiedener<br />
algebraischer Bäume genügend große Entfernungen voneinander haben.<br />
Wichtig ist, daß in diesem Schritt nur vorhandene Metadaten ausgenutzt<br />
werden, aber noch nicht Operationen wie Sortierungen, Hashing oder der<br />
Aufbau von Indexen eingesetzt werden. Dies ist der Fall, da in der physischen<br />
Optimierung noch nicht die Möglichkeit besteht, zu entscheiden, ob<br />
sich die Investition in eine solche Operation lohnen würde. Dementsprechend<br />
erhält man verschiedene physische Prototypen, die genügend unterschiedlich<br />
sind (z. B. genau einen Prototyp), die aber alle eines gemeinsam<br />
haben, sie nutzen nur existierende Strukturen aus. Für die Übersetzungen in<br />
physische Prototypen sind natürlich die Angaben über vorhandene Indexe,<br />
Sortierungen etc. unverzichtbar.<br />
Kostenbasierte Optimierung: Die kostenbasierte Optimierung erhält mehrere<br />
Prototypen, die in diesem Schritt mittels des in Kapitel 4 entwickelten<br />
Kostenmodells <strong>und</strong> eines angemessenen Suchverfahrens aus Kapitel 5 weiter<br />
optimiert werden sollen. Die nächsten Abschnitte dieses Kapitels sind<br />
der Erklärung der Gesamtstrategie gewidmet.<br />
Ausgehend von einem physischen Prototypen soll die Generierung eines<br />
neuen Operatorbaumes vollzogen werden, die insbesondere die Erzeugung<br />
von zusätzlichen Strukturen wie Partitionierungen, Sortierungen <strong>und</strong> Inde-<br />
75
76 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
xen in Betracht zieht. Dies ist in der kostenbasierten Optimierung sinnvoll,<br />
da mittels des Kostenmodells eine Gr<strong>und</strong>lage vorliegt, auf der entschieden<br />
werden kann, ob das Hinzufügen einer solchen Operation weniger kostet,<br />
als die Einsparungen, die man damit erreicht.<br />
Anschließend sollen auch noch weitere Transformationen durchgeführt werden.<br />
Mit Generierung <strong>und</strong> Transformation im allgemeinen <strong>und</strong> den Vor<strong>und</strong><br />
Nachteilen beschäftigt sich der folgende Abschnitt 6.1.<br />
Im Abschnitt 6.2 wird dann die globale Strategie 2 erklärt, wohingegen im<br />
darauffolgenden Abschnitt die lokalen Gesichtspunkte herausgearbeitet werden.<br />
Wurde jeder physische Prototyp in der noch zu erklärenden Weise bearbeitet,<br />
ergeben sich verschiedene physische Operatorbäume, deren Kosten<br />
bekannt sind. Über diese Kosten muß nunmehr nur noch das Minimum gebildet<br />
werden, um den optimalen Ausführungsplan zu erhalten, dieser wird<br />
nun weitergegeben.<br />
Ausgabe: Dieser Schritt erklärt sich fast von selbst. Es wird ein Ausführungsplan<br />
übergeben, dieser wird ausgeführt <strong>und</strong> das Endergebnis dem Benutzer<br />
zur Verfügung gestellt.<br />
6.1 Transformation <strong>und</strong> Generierung von physischen<br />
Anfragebäumen<br />
Bislang erwartet man, daß die kostenbasierten Optimierung generell wie folgt<br />
abläuft. Man hat einen Prototypen als Startwert, mit dem ein heuristisches<br />
Suchverfahren begonnen wird. Ein solches Suchverfahren benötigt einen Nachbarschaftsbegriff.<br />
Dieser ist durch die Anzahl von Transformationen gegeben. Das<br />
heißt man transformiert einen Anfragebaum, bewertet die verschiedenen Transformationen<br />
mittels der Kostenfunktion <strong>und</strong> wählt die günstigste Alternative aus.<br />
Bei diesem Verfahren stößt man aber auf Probleme, die eine Ausführung in dieser<br />
Form unmöglich machen. Diese werden im folgenden Abschnitt genauer erklärt.<br />
Nachdem nun also die Transformation nicht zum Ziel führt, muß ein anderes<br />
Verfahren benutzt werden, das der Generierung. Hier ist die Herangehensweise<br />
eine andere, es werden nicht Bäume transformiert, sondern Schritt für Schritt<br />
benötigte Teilbäume eines Anfragebaumes erzeugt bzw. zusammengefügt. Dies<br />
wird in der zweiten Hälfte des Abschnittes erklärt.<br />
2 Die globale Strategie beschreibt die Optimierung des gesamten Anfragebaumes, wohingegen<br />
die lokale nur einen Teilabschnitt verbessert.
6.1. TRANSFORMATION UND GENERIERUNG 77<br />
Da auch die Generierung nicht problemlos läuft, soll im Endeffekt ein Verfahren<br />
verwendet werden, welches eine Mischung dieser beiden darstellt. Dieses Verfahren<br />
wird im nächsten Abschnitt entwickelt. Dabei wird zwischen lokaler <strong>und</strong><br />
globaler Strategie unterschieden, die lokale entspricht der Transformation von<br />
Teilbäumen, die globale eher einer Generierung. Die Erklärungen im folgenden<br />
Abschnitt können daher nur eine kleine Einführung in die Transformation bzw.<br />
Generierung sein (siehe dazu Markoette [38]). Die eigentliche Anwendung wird<br />
erst später genauer beschrieben.<br />
• Transformation<br />
Durch die Anwendung von Transformationsregeln wird ein vollständiger<br />
physischer Anfragebaum in einen weiteren überführt. Diese Vorgehensweise<br />
wird auch als Verbesserungsverfahren bezeichnet. Leider ist dieser Name<br />
leicht irreführend, da zwischenzeitlich auch schlechtere Varianten erzeugt<br />
werden können. Der entscheidende Punkt ist, daß ein Baum als Startwert<br />
gewählt wird <strong>und</strong> von diesem aus Transformationsregeln angewandt werden.<br />
Abbildung 6.2: Schematische Darstellung der Transformation eines Anfragebaums<br />
Als Startwert würde hier einer der physischen Prototypen genommen (1),<br />
um danach durch Anwendung der Transformationsregeln der physischen Algebra<br />
in verschiedene andere Darstellungen überführt zu werden; (2) <strong>und</strong><br />
(3) (siehe dazu Kapitel 2.1). Eine solche Transformation könnte beispielsweise<br />
wie folgt aussehen (siehe Abbildung 6.3).<br />
Wie wir aus dem Abschnitt 2.1 wissen, lassen sich im Prinzip alle Transformationsregeln<br />
in zwei Richtungen anwenden. In Bild 6.2 ist die Rückpfeilspitze<br />
jeweils schwächer dargestellt, um die wenigen Ausnahmen anzudeuten.
78 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
Darüberhinaus werden auch keine Rücktransformationen durchgeführt, da<br />
die Idee des Verfahrens darin liegt, daß Transformationen durchgeführt werden,<br />
um die Nachbarschaft eines Prototypen mittels eines kostenbasierten<br />
Suchalgorithmus’ zu untersuchen.<br />
✶NestedLoop ✪✪ ❡ R1 R2<br />
✲<br />
✶Merge ✪✪ ❡ Sort(·) Sort(·)<br />
Abbildung 6.3: Beipiel für die Transformation eines Anfragebaums<br />
Das Problem dieser Idee liegt in der Tatsache, daß zwar zu jedem so erhaltenen<br />
Baum die Kosten berechnet werden können, aber keinerlei Aussagen<br />
darüber getätigt werden können, wie weit das Optimum, gemessen in Transformationsschritten,<br />
von der aktuellen Repräsentation entfernt liegt (siehe<br />
Kapitel 5). Dies ist aber Voraussetzung dafür, nicht Erfolg versprechende<br />
Transformationen ausschließen zu können. Ohne dieses Ausschließen von<br />
” schlechten“ Transformationen ist es aber nicht möglich, die Suche auf einen<br />
sinnvollen Teilraum einzuschränken. Damit wäre nur noch eine vollständige<br />
Suche in der Lage, schlechte Pläne zielsicher zu vermeiden, diese soll aber<br />
selbstverständlich vermieden werden.<br />
Als Ausnahmen hiervon sind stochastische Optimierungsverfahren zu nennen,<br />
welche aber gewisse Charakteristika des Suchraumes benötigen, die<br />
hier nicht vorausgesetzt werden.<br />
• Generierung<br />
Bei der Generierung werden, beginnend mit den Blättern, also den benötigten<br />
Relationen, (es wurde noch kein Ausführungsplan erzeugt) (1) sukzessive<br />
benötigte Operatoren in die Menge der bereits generierten Teilbäume<br />
eingebaut (2), bis eine vollständige interne Repräsentation gef<strong>und</strong>en worden<br />
ist (3). Jeder eingebaute Operator wird nicht wieder entfernt oder verschoben.<br />
Je weiter die Generierung gediehen ist, desto weniger Alternativen sind<br />
von der aktuellen Teilrepräsentation aus erzeugbar (siehe Abbildung 6.4).<br />
Geht man von einem Zwischenergebnis der Generierung aus, so ist die Menge<br />
aller aus dieser Teilrepräsentation generierbaren Operatorbäume eine<br />
Obermenge der Anfragebäume, die nach dem nächsten Generierungsschritt<br />
entwickelt werden können.<br />
Wendet man dieses Verfahren z. B. zum Optimieren einer Joinreihenfolge<br />
an, erreicht man hierdurch gegenüber einer erschöpfenden Suche eine Ver-<br />
R1<br />
R2
6.1. TRANSFORMATION UND GENERIERUNG 79<br />
Abbildung 6.4: Schematische Darstellung der Generierung eines Anfragebaums<br />
besserung der Komplexität von n! auf 2 n , wenn aufgr<strong>und</strong> von Kommutativität<br />
äquivalente, aber kostenaufwendigere Alternativen nicht betrachtet<br />
werden (siehe dafür Moerkotte [38]).<br />
Darüberhinaus hat man bei dieser Vorgehensweise Informationen über die<br />
noch entstehenden Kosten, da die Menge der nicht eingebauten Operatoren<br />
vorliegt. Könnte man dabei die Kosten nach unten abschätzen, lassen<br />
sich effiziente Suchalgorithmen wie der A*-Algorithmus benutzen (dieser<br />
ist nachweislich das beste Suchverfahren bei gleicher Informiertheit aller<br />
Suchmethoden 3 ; siehe dazu Russel <strong>und</strong> Norvig [45]).<br />
In jedem Schritt der Generierung hat man also eine Menge von Teilbäumen<br />
gegeben, die jeder für sich fest gehalten werden. Darüberhinaus hat man<br />
eine Menge von Operatoren gegeben, die noch einzuarbeiten sind, um eine<br />
vollständige Repräsentation zu erhalten. Hieraus muß ein passender Operator<br />
nach Minimierungsgesichtspunkten der anfallenden Kosten für die Bearbeitung<br />
der Anfrage ausgewählt werden <strong>und</strong> in sinnvoller Weise in die<br />
Teilbäume eingebaut werden.<br />
Dabei kann der Operator entweder als neue Wurzel über der alten Wurzel<br />
eines Teilbaumes eingesetzt werden, falls es sich um einen einstelligen<br />
Operator handelt, oder aber ein zweistelliger Operator wird so eingefügt,<br />
daß zwei vorhandene Bäume zu einem großen Baum verb<strong>und</strong>en werden,<br />
der die vorhandenen als rechten <strong>und</strong> linken Teilbaum <strong>und</strong> den Operator als<br />
3 Da man aber in der relationalen Anfrageoptimierung den besten Anfragebaum nicht kennt,<br />
können diese Algorithmen nie angewandt werden.
80 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
Wurzel enthält. Dadurch entsteht eine neue Menge von Teilbäumen <strong>und</strong> die<br />
Operatormenge wird um eins reduziert.<br />
❥<br />
✁❆<br />
✁✁ ❆ ❥<br />
✁❆<br />
✁✁ ❆❆<br />
✁❆<br />
✁✁ ❆ ❥<br />
�� ❅<br />
✁❆<br />
✁✁ ❆ Abbildung 6.5: mögliche Schritte der Generierung<br />
❥<br />
�<br />
�❅<br />
❅<br />
✁❆<br />
✁❆<br />
✁✁ ❆❆ ✁✁ ❆❆<br />
Das Problem hierbei liegt in der Frage, in welcher Reihenfolge welche Operatoren<br />
wo eingefügt werden sollen, um einen korrekten Anfrageplan zu<br />
erhalten. Denn ein Operator, der in der Menge der noch einzufügenden<br />
Operatoren liegt, kann natürlich nicht völlig beliebig hinzugefügt werden.<br />
Um dieses Problem zu lösen, wird der Prototyp vorgegeben. Es sei schon<br />
gesagt, daß für jeden Prototypen eine Generierung durchgeführt werden<br />
soll, bei der der Prototyp sozusagen als ” Peilung“ eingesetzt wird. Einzelne<br />
Teilbäume des Prototypen sollen hierbei transformiert werden <strong>und</strong> dann<br />
als Teilbaum in den neu zu generierenden Baum eingesetzt werden.<br />
6.2 Die globale kostenbasierte Optimierungsheuristik<br />
Für die kostenbasierte Optimierungsheuristik sei ein physischer Prototyp vorgegeben.<br />
Dieser gibt einen möglichen Ausführungsplan der Anfrage wieder, der als<br />
Ausgangspunkt der Generierung dienen soll. Der Prototyp legt erstens fest, welche<br />
Operatoren einzusetzen sind, um tatsächlich einen Anfrageplan für die gegebene<br />
Anfrage zu erhalten, wobei natürlich Operatoren durch äquivalente ersetzbar<br />
sind. Zweitens legt er fest, in welcher Reihenfolge die Operatoren überhaupt anwendbar<br />
sind, um die korrekte Anfrage zu modellieren.<br />
Die Idee liegt jetzt darin, gr<strong>und</strong>sätzliche Baumstruktur des gegebenen Protoypen<br />
festzulassen, aber durch das Einfügen von einstelligen Operatoren (z. B. Hash),<br />
die Nutzung von günstigeren physischen Operatoren zu ermöglichen. Dabei muß<br />
natürlich überprüft werden, in welchem Verhältnis die neu entstandenen Kosten<br />
zu den eingesparten stehen.<br />
Da für die Generierung ein passender bottom-up-Durchlauf des Prototypen benötigt<br />
wird, soll zunächst die folgende Aussage über den Prototyp gemacht werden.
6.2. DIE GLOBALE KOSTENBASIERTE OPTIMIERUNGSHEURISTIK 81<br />
Jeder Prototyp, der eine Anfrage auf mindestens zwei Relationen repräsentiert 4 ,<br />
ist ein Baum der Form:<br />
[π]<br />
[σ]<br />
✶<br />
✪ ❡<br />
✪ ❡<br />
T1<br />
Abbildung 6.6: lokale Form des Prototypen<br />
Dabei seien die Ti entweder Relationen oder selber Bäume der gleichen Form.<br />
Dazu sei angemerkt, daß natürlich mit π, σ <strong>und</strong> ✶ nicht die algebraischen Operatoren<br />
dieses Namens gemeint sind, sondern eine der physischen Übersetzungen.<br />
Darüberhinaus sind die eckigen Klammern dahingehend zu verstehen, daß ein<br />
solcher Operator auch fehlen kann.<br />
Diese Form wird erreicht, da erstens jede zweistellige Operation als passender<br />
Join dargestellt werden kann <strong>und</strong> wird, <strong>und</strong> zweitens eine beliebige Kette von einstelligen<br />
Operatoren, also Projektionen <strong>und</strong> Selektionen, im physischen Prototyp<br />
derart zusammengefaßt wird, daß nur noch eine Selektion vor einer Projektion<br />
durchgeführt wird. Wird nach true selektiert bzw. auf das Schema projiziert,<br />
wird die entsprechende Operation weggelassen.<br />
Das Entscheidende an dieser Form ist, daß der Baum einem binären Baum sehr<br />
nahe kommt. Das soll heißen, es gibt nur Knoten mit weniger als zwei Kindern<br />
<strong>und</strong> die Höhe der linearen Teilgraphen ist durch zwei begrenzt.<br />
Jetzt soll die Generierung erklärt werden.<br />
Initialisierung: Zur Initialisierung wird ein Zeiger auf den Knoten K gesetzt,<br />
der genau zwei Kinder besitzt <strong>und</strong> unter allen Knoten mit dieser Eigenschaft<br />
das höchste Level hat, also den größten Abstand von der Wurzel, <strong>und</strong> am<br />
weitesten links steht, falls gleiche Level auftreten. Dieser Zeiger hat die<br />
Aufgabe, den bereits fertig generierten Teil des Operatorbaumes von dem<br />
restlichen Prototypen zu trennen.<br />
Rekursion: In jedem Schritt der Generierung findet folgender Ablauf statt. Zunächst<br />
wird die sogenannte lokale Umgebung des Knotens K im Prototypen<br />
bestimmt, diese ist von der Form (siehe Abbildung 6.7) für passende<br />
physische Operatoren π <strong>und</strong> σ mit entsprechenden Bedingungen bzw.<br />
4 Ist dies nicht der Fall, handelt es sich um einen der beiden trivialen Fälle: entweder ist der<br />
Baum leer, oder er degeneriert zu einem linearen Graphen.<br />
T2
82 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
K ❧<br />
✪✪ ❡ [π] [π]<br />
[σ] [σ]<br />
Abbildung 6.7: lokale Umgebung<br />
Attributmengen. Dieser Teilbaum wird nach der lokalen Strategie, die im<br />
nächsten Abschnitt erklärt wird, auf verschiedene Weisen optimiert <strong>und</strong><br />
jeweils nach <strong>und</strong> nach in einer Kopie (inklusive des Zeigers) des Prototypen<br />
durch diese Optimierung ersetzt. Wurden in der lokalen Optimierung<br />
zusätzliche Metadaten erzeugt (dies wird i.a. der Fall sein), wird der Pfad<br />
vom Knoten K zur Wurzel in der Kopie bottom-up dahingehend überprüft,<br />
ob ein physischer Operator durch einen äquivalenten mit besserer Laufzeit<br />
ersetzt werden kann. Dieser Teil der Strategie soll die von einer lokalen Optimierung<br />
induzierte globale Optimierung genannt werden. Hierbei wird,<br />
wie in der physischen Optimierung, ein Hash-Join einem Index-Index-Join<br />
vorgezogen, gefolgt von Index-Rel-, Merge- <strong>und</strong> NestedLoop-Join.<br />
Im gleichen Durchlauf können die Kosten des neu entstehenden Baumes<br />
berechnet werden, da sich außerhalb des Pfades vom Knoten K zur Wurzel<br />
die Kosten nicht verändern. Das bedeutet, daß in jedem besuchten Knoten<br />
auf diesem Pfad, die Kosten des Teilbaumes, aus dem heraus man den<br />
Knoten besucht, <strong>und</strong> die Kosten an diesem Knoten (nach einer eventuellen<br />
Ersetzung durch einen äquivalenten Operator) inklusive der für die<br />
Kostenberechnung benötigten Parameter, berechnet <strong>und</strong> in dem Knoten<br />
abgespeichert werden.<br />
Nachdem alle lokalen Optimierungen, die sinnvoll erscheinen, getätigt wurden<br />
<strong>und</strong> die entsprechenden Kosten vorliegen, wird unter allen der Plan mit<br />
den geringsten globalen Kosten ausgewählt. Insbesondere werden also nicht<br />
die Kosten der lokalen Optimierungen miteinander verglichen. Dieser Plan<br />
wird durch eine modifizierte Kopie des Prototypen gegeben, in der insbesondere<br />
die beste vorgenommene Optimierung der Umgebung des Knotens<br />
K enthalten ist. Diese Kopie nimmt nun den Platz des alten Prototypen<br />
ein.<br />
Als nächstes wird der Zeiger (auf der Kopie) auf den nächsten Knoten gesetzt.<br />
Dieser wird dadurch bestimmt, daß der begonnene Level-Durchlauf<br />
(bottom-up von links nach rechts) des Prototypen fortgeführt wird. Dies ist<br />
trotz der Modifikation des Prototypen wohldefiniert, da die lokalen Optimierungen<br />
sich nur auf den Knoten K <strong>und</strong> darunterliegende Level auswirken<br />
<strong>und</strong> die induzierte globale Optimierung keine Veränderungen an der Baum-
6.2. DIE GLOBALE KOSTENBASIERTE OPTIMIERUNGSHEURISTIK 83<br />
struktur bewirkt. (Es werden hier nur die Operatoren in einzelnen Knoten<br />
verändert.)<br />
Dadurch wird die vorgenommene lokale Optimierung fixiert, was dahingehend<br />
interpretiert werden kann, daß die entsprechende, optimierte Umgebung<br />
in die (hier nicht realisierte) Menge der fixierten Teilbäume der<br />
Generierung eingefügt wird. Außerdem wird dadurch die Menge der einzufügenden<br />
Operatoren (in Form von Umgebungen von Joinknoten), um<br />
den abgearbeiteten Teil verringert.<br />
Gesetzt den Fall es gibt keinen weiteren Knoten mit zwei Kindern, kann<br />
die Rekursion abgebrochen werden <strong>und</strong> die Abschlußphase übergegangen<br />
werden.<br />
Terminierung: Oberhalb des obersten Knotens mit zwei Kindern kann maximal<br />
noch eine Selektion <strong>und</strong> eine Projektion liegen. In einem abschließenden<br />
Schritt wird überprüft, ob sich das Erzeugen eines Index auf dem Ergebnis<br />
des letzten Joins lohnt, in dem in einer Kopie des Prototypen diese Operation<br />
eingefügt wird, Selektion <strong>und</strong> Projektion mittels σ Index bzw. π Index<br />
ausgeführt werden <strong>und</strong> die resultierenden Kosten des gesamten Baumes<br />
berechnet werden. (Dafür müssen natürlich nur die Kosten der Indexierung,<br />
der Index-Selektion <strong>und</strong> -Projektion auf die Gesamtkosten im letzten<br />
Joinknoten addiert werden.) Der günstigere der beiden Pläne wird dann<br />
ausgegeben.<br />
Diese Strategie entspricht einem leicht modifizierten Hill-Climbing-Algorithmus.<br />
Zunächst eine kurze Zusammenfassung des Hill-Climbing: Ausgehend von einem<br />
Startwert werden mittels eines festen Nachbarschaftsbegriffes alle Nachbarn des<br />
aktuellen Knoten bewertet <strong>und</strong> der beste ausgesucht. Ist dieser nicht besser als<br />
der aktuelle Knoten, wird der aktuelle Knoten ausgegeben, andernfalls wird der<br />
neue Knoten als aktueller Knoten gespeichert <strong>und</strong> von dort die Suche fortgesetzt.<br />
Nun zur vorgestellten Generierung: Der vorgegebene Prototyp der physischen<br />
Optimierung bildet den Startwert der Suche. Für einen festen Knoten K mit<br />
genau zwei Kindern ist die Nachbarschaft des aktuellen Prototypen gegeben durch<br />
die modifizierten Kopien des Prototypen (nach lokaler <strong>und</strong> induzierter globaler<br />
Optimierung) <strong>und</strong> den Prototypen selbst. Unter diesen Nachbarn (inklusive des<br />
aktuellen Prototypen) wird das Minimium der Kosten gebildet, der entsprechende<br />
Baum wird zum aktuellen Prototypen. Durch das Verschieben des Zeigers wird<br />
nun aber der Nachbarschaftsbegriff verändert. Denn falls der alte Prototyp unter<br />
allen Nachbarn selbst die minimalen Kosten aufweist, bleibt er der Prototyp, aber<br />
im nächsten Schritt werden nicht wieder die gleichen Nachbarn erzeugt, sondern<br />
solche, die durch lokale Optimierungen einer anderen Umgebung (nämlich der<br />
des neuen Knotens K) entstehen. Abgebrochen wird der Algorithmus, wenn man<br />
die Wurzel erreicht.
84 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
6.3 Die lokale kostenbasierte Optimierungsheuristik<br />
Ausgehend von einer in Abschnitt 6.2 gegebenen lokalen Umgebung eines Joinknoten<br />
K der Form (siehe Abbildung 6.8), werden wie folgt Transformationen<br />
erstellt:<br />
(a) Durch zusätzliche Sortierungen unterhalb des Knotens K, falls die Relationen<br />
an dieser Stelle noch nicht sortiert sind, kann der Joinoperator im<br />
Knoten K durch einen Merge-Join ersetzt werden. Der durch diese Transformation<br />
entstandene Teilbaum ist eine der lokalen Optimierungen, die an<br />
die globale Heuristik übergeben wird.<br />
K ❧<br />
✪✪ ❡ [π] [π]<br />
[σ] [σ]<br />
Abbildung 6.8: lokale Umgebung<br />
(b) Vergleichbar zur Sortierung werden zusätzliche Partionierungen der zu<br />
bearbeitenden Anfangsrelationen in den Baum unterhalb des Knoten K<br />
eingefügt, um hierbei einen Hash-Join im Knoten K generieren zu können.<br />
Auch dieser Baum wird der Heuristik zurückgegeben.<br />
(c) Als nächster Schritt wird der Anfragebaum auf kostensparendes Einfügen<br />
von Indexen untersucht. Dabei ist es wichtig, daß frühzeitiges Erzeugen<br />
die beste Art ist, Zugriffskosten zu verringern. Falls auf der Relation unter<br />
einer der Selektionen kein Index vorhanden ist, wird direkt unter der<br />
Selektion die Operation T ree, also der Aufbau eines Indexes, eingefügt. Dadurch<br />
können alle Selektionen bzw. Projektionen durch ihre kostensparende<br />
Index-Variante ersetzt werden. Ebenso kann ein Index-Index-Join verwendet<br />
werden, um danach wiederum den Teilbaum an die globale Heuristik<br />
zurückzugeben.<br />
Nicht vergessen darf man, daß nicht jeder Index sinnvoll sein muß. Wird<br />
zum Beispiel die Attributmenge mit einem Index versehen, bei dem das<br />
Attribut den nachfolgenden Knoten nicht mehr erreicht, verringern sich die<br />
Einsparungspotentiale. Dieses gilt natürlich auch für alle anderen Optimierungsmethoden.
6.4. FAZIT DER KOSTENBASIERTEN OPTIMIERUNG 85<br />
6.4 Fazit der kostenbasierten Optimierung<br />
Es werden zwar bei der Entwicklung der physischen Prototypen keine Hash-Joins<br />
generiert (es kommt selten vor, daß Relationen schon vorgehashed vorliegen), aber<br />
jetzt bei der kostenbasierten Suche ergeben sie dafür mehr Sinn. Dieses Verhalten<br />
läßt sich gut auch bei anderen Optimierern, wie dem Oracle-Optimierer erkennen.<br />
Auch dort werden anfangs kaum Hash-Joins generiert, sondern erscheinen erst<br />
bei einer kostenbasierten Optimierung, die interessanterweise ein Kostenmodell<br />
besitzt, das auf Histogrammen <strong>und</strong> Stichproben aufbaut.<br />
Das in dieser Diplomarbeit gewählte Kostenmodell bietet einen entscheidenden<br />
Vorteil. Das Scannen einer Relation mit Hilfe eines Indexes ist laufzeitmäßig<br />
in keinem Fall wesentlich schlechter als ein einfacher Relationen-Scan (in vielen<br />
Fällen ist es wesentlich besser), da es erkennt, ob es sich lohnt einen Zugriff über<br />
den Index durchzuführen, oder einfach die gesamte Relation hineinzuladen. Dies<br />
schlägt sich in der Kostenfunktion in der Minimumbildung nieder. Falls das Laden<br />
der gesamten Relation niedrigere Kosten erzeugt als der Zugriff über den Index,<br />
wird dies erkannt <strong>und</strong> die Laufzeit verschlechtert sich gegenüber dem Relationen-<br />
Scan nur um das Laden des Indexes. Zwar gibt es ein paar Sonderfälle, bei denen<br />
es teurer werden könnte, aber die fallen nicht ins Gewicht, da sie sich maximal<br />
nur um das Reinladen des Indexes unterscheiden (siehe Scan-Kosten 4.6.1).<br />
Abschließend sei noch eine Optimierungsstrategie erwähnt, die in unserem Kostenmodell<br />
keine Vorteile besitzt:<br />
(a) Nämlich das Einfügen von zusätzlichen Projektionen. Dadurch werden<br />
zwar die Anzahl der Zwischenergebnisse nicht geringer, aber es werden I/O-<br />
Zugriffe eingespart, da die Tupellängen verkürzt werden können. Auch diese<br />
Regel sollte möglichst früh ausgeführt werden, da sie verhindert, daß man<br />
red<strong>und</strong>ante Daten in die oberen Knoten mit sich führt.<br />
Schließlich seien hier noch ein paar Transformationsregeln erwähnt, die in speziellen<br />
Fällen eine weitere Optimierung ermöglichen. Diese Methoden benötigen<br />
noch zusätzliche Informationen, die ein komplettes Betrachten des Anfragebaumes<br />
erfordern. Die Anwendung ist je nach Anfrage während oder vor der Generierungsphase<br />
sinnvoll <strong>und</strong> sollte in jedem Fall neu untersucht werden.<br />
(a) Der Optimierer versucht zusätzliche Transformationen durchzuführen, um<br />
gleiche Zwischenergebnisse zu generieren, die er wiederum red<strong>und</strong>ant<br />
entfernen kann, da er sie ja nur einmal berechnen muß.<br />
(b) Die in die Blätter verschobenen Selektionen können dahingehend überprüft<br />
werden, ob sie vielleicht an einem anderen Ort im Anfrageplan weniger<br />
Kosten verursachen würden.
86 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
Dies kann z. B. sinnvoll sein, wenn die Selektion die Nutzung eines Index in<br />
einer nachfolgenden Operation verhindert (siehe Beispiel 3.9).<br />
(a) Das Anordnen der Joins kann in der physischen Optimierung überdacht<br />
werden. Dabei sollen die Zwischenergebnisse minimiert werden. Tatsächlich<br />
sollte diese Regel schon bei der algebraischen Optimierung stattfinden. Leider<br />
ist der dortige Aufwand um ein Vielfaches größer, da die Daten für die<br />
Abschätzung der Relationengrößen hier nicht vorliegen, weshalb es sinnvoller<br />
ist, diese Regel in der physischen Optimierung durchzuführen. Als eine<br />
kleine Heuristik könnte man in diesem Fall die Anordnung der Relationen<br />
nach der Anzahl ihrer Tupel vornehmen, so daß sich die Zwischenergebnisse<br />
verkleinern. In der kostenbasierten Optimierung könnte diese Anordnung<br />
noch mittels des Kostenmodels detaillierter überprüft werden.<br />
6.5 Beispiel zur kostenbasierten Suche<br />
Erinnern wir uns an unser Beispiel aus Kapitel 2.2 mit der folgenden relationalen<br />
Anfrage: πB,D(σ(R1.A=c∧R2.E=2∧R1.C=R2.C)(R1 × R2))<br />
πB,D<br />
×<br />
✓ ❙<br />
✓ ❙<br />
R1<br />
σ (R1.A=c∧R2.E=2∧R1.C=R2.C)<br />
R2<br />
Abbildung 6.9: 1:1-übersetzter Anfragebaum<br />
Nach der Anwendung der algebraischen Heuristik (siehe Abschnitt 2.3), bekommen<br />
wir folgenden Anfragebaum: πB,D(σR1.A=c(R1) ✶ σR2.E=2(R2))<br />
πB,D<br />
✶R1.C=R2.C<br />
✪ ❡<br />
σ<br />
✪ ❡<br />
(R1.A=c) σ (R2.E=2)<br />
R1<br />
R2<br />
Abbildung 6.10: Algebraischer Prototyp<br />
Desweiteren seien wiederum die Metadaten für R1 wie folgt gegeben:
6.5. BEISPIEL ZUR KOSTENBASIERTEN SUCHE 87<br />
Metadaten Wert<br />
page(R1) 1000<br />
|R1| 10000<br />
n(A, R1) 10000<br />
n(B, R1) 10<br />
n(C, R1) 100<br />
Tabelle 6.1: Metadaten für die Relation R1<br />
Für R2 sei das Attribut C der Schlüssel der Relation <strong>und</strong> auf E liege ein Index<br />
vor. Außerdem seien die dazugehörigen Metadaten:<br />
Metadaten Wert<br />
page(R2) 1000<br />
|R2| 10000<br />
n(C, R2) 10000<br />
n(D, R2) 10<br />
n(E, R2) 100<br />
|IndexE(R2)| 4<br />
page(IndexE(R2)) 50<br />
Tabelle 6.2: Metadaten für die Relation R2<br />
Somit ergibt sich durch die Anwendung unserer in Abschnitt 3.8 vorgestellten<br />
Heuristik folgender physischer Prototyp:<br />
σ Rel<br />
(R1.A=c)<br />
π IndexDup<br />
B,D<br />
✶ Rel−Index−Join<br />
R1.C=R2.C<br />
✪ ❡<br />
✪ ❡<br />
✪<br />
✪<br />
R1<br />
σ Index<br />
(R2.E=2)<br />
R2<br />
Abbildung 6.11: Physischer Prototyp<br />
Wenn wir wiederum ein I/O-Constraintes System mit W = 0, 02 voraussetzen,<br />
dann bekommen wir die Kosten für diesen Anfragebaum wie folgt: 5<br />
5 Die vollständige Rechnung wird dem Leser erspart, da sie keine neuen Erkenntnisse liefert.<br />
(siehe Beispiel 4.8)
88 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
cost(σRel (R1.A=c) ) = 1000 + 0, 02 · 10000 = 1200<br />
cost(σIndex 1<br />
1<br />
(R2.E=2) ) = (50 + min(1000, · 1000 · 4)) + 0, 02 · ( · (10000 + 4)) = 92<br />
100 100<br />
cost(✶ Rel−Index−Join<br />
R1.C=R2.C ) = (0 + 90) + 0, 02 · ( 1 · (1 + 100)) = 90<br />
100<br />
cost(π IndexDup<br />
B,D ) = 0 + 0, 02 · 100 = 2<br />
cost(P) = 1200 + 92 + 90 + 2 = 1384<br />
Jetzt startet die kostenbasierte Suche, indem sie einen neuen Anfragebaum generiert.<br />
Anfangs betrachtet er den untersten Join mit den dazugehörigen Kindern. Dabei<br />
untersucht er die beiden Selektionen <strong>und</strong> erkennt, daß bei R2 keine Optimierung<br />
durchgeführt werden muß (Index schon vorhanden). Daraufhin wird bei R1 die<br />
Generierung eines sinnvollen Indexes mittels T ree(R) berechnet:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
page(R1) · log2(page(R1)) |R1| · log2(|R1|) I/O + 0, 02· CPU<br />
1000 · log2(1000) 10000 · log2(10000) (1000 · 9, 966) + (0, 02·<br />
10000 · 13, 288) ≈ 16610<br />
Da die Kosten für den Aufbau eines Indexes auf R1 größer sind als die Gesamtkosten,<br />
wird die Indexierung verworfen. Darüberhinaus wird auch das Sortieren<br />
nicht näher betrachtet, da Indexierung vergleichbare Kosten wie eine Sortierung<br />
aufwirft.<br />
Als nächster Schritt nimmt sich der Algorithmus den darüberliegenden Join <strong>und</strong><br />
untersucht ihn auf kostensparendes Einfügen von Funktionen. Dabei wird zuerst<br />
das Hashen der Ergebnisse aus den jeweiligen vorherigen Selektionen berechnet.<br />
Dabei wird zur Berechnung die Selektivität von σR1.A=c(R1) := � R1 benötigt, die<br />
man - wie im Kapitel 4.3.1 erwähnt - aus der Kardinalität des Wertebereichs des<br />
Attributes A bekommt:<br />
q(σA=c(R1)) =<br />
⇔ | � R1| = |R1|<br />
n(A, R1)<br />
1<br />
n(A, R1)<br />
10000<br />
= = 1<br />
10000<br />
Damit hat man folgende Hashkosten für � R1 = σR1.A=c(R1), dabei darf man nicht<br />
vergessen, daß keine I/O-Kosten anfallen, da das Ergebnis aus der relationalen<br />
Selektion gepiplined wird:
6.5. BEISPIEL ZUR KOSTENBASIERTEN SUCHE 89<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
0 | � R1| I/O + 0, 02· CPU<br />
0 1 0 + 0, 02 · 1 ≈ 0<br />
<strong>und</strong> folgende Hashkosten für � R2 = σ Index<br />
(R2.E=2) (R2):<br />
q(σE=2(R2)) =<br />
⇔ | � R2| = |R2|<br />
n(E, R2)<br />
1<br />
n(E, R2)<br />
= 10000<br />
100<br />
= 100<br />
Vorausgesetzt, daß durchschnittlich 10 Tupel pro Seite gespeichert werden, folgt<br />
dann:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
page(�R)<br />
55 · | � R| I/O + 0, 02· CPU<br />
10<br />
10<br />
55 100 55 + 0, 02 · 100 ≈ 2<br />
Daraus folgt, daß ein zusätzliches Hashen der Selektionsergebnisse Kosten in Höhe<br />
von 2 Punkten verursacht. Damit könnte man jetzt den Rel-Index-Join durch<br />
einen Hash-Join ersetzen, der folgendes kostet:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
0 + Scan Rel (Hash( � R2)) q( � R1 ✶�R1.C=�R2.C � R2) · (| � R1| · | � R2|) I/O + 0, 02· CPU<br />
0 + 0 = 0<br />
1<br />
100 · (1 · 100) = 1 (0 + 0, 02 · 1) ≈ 0<br />
Insgesamt würden also für die beiden Hashes gefolgt von dem Hash-Join folgende<br />
Kosten entstehen<br />
0 + 2 + 0 = 2
90 KAPITEL 6. DIE OPTIMIERUNGSSTRATEGIE<br />
Dafür würde man sich den Rel-Index-Join sparen, der folgendes kostet:<br />
I/O-Kosten CPU-Kosten Ergebnis<br />
0 + Scan Index<br />
p (R2) q( � R1 ✶�R1.C=�R2.C � R2) · (| � R1| + | � R2|) I/O + 0, 02· CPU<br />
0 + 90 = 90<br />
Schließlich bekommen wir für diesen Anfragebaum:<br />
Kosten in Höhe von:<br />
1<br />
100 · (1 + 100) ≈ 1 (90 + 0, 02 · 1) ≈ 90<br />
π IndexDup<br />
B,D<br />
✶HashJoin R1.C=R2.C<br />
✪ ❡<br />
✪ ❡<br />
✪<br />
✪<br />
Hash(·) Hash(·)<br />
σ Rel<br />
(R1.A=c)<br />
R1<br />
σ Index<br />
(R2.E=2)<br />
Abbildung 6.12: Kostenoptimierter Anfragebaum<br />
R2<br />
cost(σRel (R1.A=c) ) = 1000 + 0, 02 · 10000 = 1200<br />
cost(σIndex 1<br />
1<br />
(R2.E=2) ) = (50 + min(1000, · 1000 · 4)) + 0, 02 · ( · (10000 + 4)) = 92<br />
100 100<br />
cost(Hash(σRel (R1.A=c) )) = 0 + 0, 02 · 1 ≈ 0<br />
cost(Hash(σIndex 10<br />
1<br />
(R2.E=2) )) = + 0, 02 · 1 · · 100 = 2<br />
55 100<br />
cost(✶ Hash−Join<br />
R1.C=R2.C ) = 0 + 0, 02 · 1 ≈ 0<br />
cost(π IndexDup<br />
B,D<br />
= 0 + 0, 02 · 100 = 2<br />
cost(P) = 1200 + 92 + 0 + 2 + 0 + 2 = 1298<br />
erhalten. Das sind Einsparungen von 86 Punkten (1486 ⇒ 1298).<br />
Somit fügt der Optimierer die beiden Hashfunktionen ein <strong>und</strong> liefert den verbesserten<br />
Anfragebaum zurück.
Kapitel 7<br />
Die Implementierung des<br />
SQL-Optimierers<br />
Fantasie ist wichtiger als das Wissen,<br />
denn das Wissen ist begrenzt.<br />
Albert Einstein<br />
In diesem Kapitel soll der Anfrageoptimierer SQLOpt vorgestellt werden, der im<br />
Zuge dieser Diplomarbeit programmiert worden ist. Er beinhaltet - in einzelnen<br />
Java-packages - die zur Optimierung benötigten Module, welche wir in den letzten<br />
Kapiteln kennengelernt haben.<br />
Insgesamt sind es 180 Klassen <strong>und</strong> Interfaces mit aussagekräftigen Namen. Deshalb<br />
sollten kommende Erweiterungen leicht implementierbar sein, da auch viel<br />
Wert auf einen modularen Aufbau gelegt worden ist. Dazu gehören das einfache<br />
Hinzufügen von weiteren Operatoren, als auch Veränderungen an den implementierten<br />
Heuristiken.<br />
In den folgenden Abschnitten werden sowohl die einzelnen Pakete, als auch die<br />
gesamte Hierachie vorgestellt. Dabei sollen aber nur die wichtigsten Klassen <strong>und</strong><br />
Interfaces aufgeführt werden, da es sonst den Rahmen dieser Diplomarbeit sprengen<br />
würde. Für weitere Informationen stehen die dazugehörigen Javadocs zur<br />
Verfügung.<br />
7.1 Die Packages des SQL-Optimierers<br />
Um die eindeutige Zuordnung der programmierten Pakete gewährleisten zu können,<br />
wurde - wie in den allgemeinen Java-Formvorschriften vorgegeben - der DNS-<br />
Name des Institutes gewählt. Darunter befindet sich der eigentliche Optimierer<br />
sopt, der wiederum in seine drei Einzelteile zerlegt wurde:<br />
(a) Das Paket gui bietet dem Benutzer eine grafische Eingabemöglichkeit.<br />
91
92 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
(b) Das Paket struc beinhaltet den eigentlichen Optimierer samt Datenstrukturen.<br />
(c) Das Paket sqlp ermöglicht die Eingabe als Standard-SQL- bzw. relationale<br />
Anfrage.<br />
Die wie folgt hierarchisch zueinander stehen: 1<br />
test<br />
de/unihannover/dbs/sopt/...<br />
gui struc sqlp<br />
search alg phys<br />
rules<br />
Abbildung 7.1: Die Packages des SQL-Optimierers<br />
Unterhalb des struc Paketes befinden sich die drei vorgestellten Optimierungsschritte<br />
mit ihren Datenmodellen.<br />
(a) Das Paket alg beinhaltet die anfangs ausgeführte algebraische Optimierung<br />
des Anfragebaumes in einen algebraischen Prototyp. Dafür werden die im<br />
Kapitel 2.1. vorgestellten Regeln im Paket alg.rules benutzt.<br />
(b) Das Paket phys ermöglicht die physische Optimierung der Anfrage hin zu<br />
einem physischen Prototyp.<br />
(c) Das Paket search ist für den letzten Optimierungsschritt verantwortlich.<br />
Dabei werden durch Kostenfunktion verschiedene Pfade verfolgt, die - je<br />
nach Zeitaufwand - selektiert <strong>und</strong> zurückgeliefert werden.<br />
Schließlich wurde zum Testen des Anfrageoptimierers ein Paket test programmiert.<br />
Darin befinden sich die verschiedenen Heuristiken, die im Laufe der Diplomarbeit<br />
untersucht worden sind.<br />
1 Dabei stellen die Pfeile die Zufriffshierarchie der einzelnen Pakete dar.
7.2. GRAFISCHE BENUTZERSCHNITTSTELLE GUI 93<br />
7.2 Grafische Benutzerschnittstelle GUI<br />
7.2.1 de.unihannover.dbs.sopt.gui<br />
Dieses Paket stellt die grafische Benutzerschnittstelle bereit. Sie ermöglicht die<br />
Eingabe einer Anfrage in einem editierbaren Fenster (siehe Abbildung 7.7). Alle<br />
weiteren Optimierungsfenster sind modal programmiert <strong>und</strong> bieten zusätzlich<br />
dazu noch die Möglichkeit zur Vergrößerung der Ansicht.<br />
AbstractListModel<br />
AbstractTableModel<br />
JPanel<br />
JComponent<br />
AttributeSetListModel<br />
AttributeValueCountTableModel<br />
MetaDataTableModel<br />
TableTableModel<br />
AttributeSetList<br />
TableTable<br />
TressTextArea<br />
Package de.unihannover.dbs.sopt.gui<br />
TreeCostTextArea<br />
JDialog<br />
AttributeAddDialog<br />
AttributeEditDialog<br />
AttributeValueCountEditDialog<br />
IndexAddDialog<br />
IndexDataDialog<br />
InfoDialog<br />
MetaDataTable MetaDataDialog<br />
SearchDialog<br />
TableAddDialog<br />
TableDataDialog<br />
TableEditDialog<br />
Abbildung 7.2: Das GUI Package des SQL-Optimierers<br />
Die grafische Schnittstelle bietet mittels des Buttons Relationen die Möglichkeit,<br />
Relationen <strong>und</strong> die dazugehörigen Attribute festzulegen. Bei der Übernahme der<br />
Eingabe werden die Objekte dann erzeugt. Dazu sind noch weitere Eingabefenster<br />
vorhanden; darunter die Maske für das Erzeugen der Metadaten der vorher<br />
festgelegten Relationen (siehe Abbildung 7.3. <strong>und</strong> 7.4.).
94 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
Klassen:<br />
Abbildung 7.3: Relationenfenster des SQL-Optimierers<br />
• AttributeSetListModel<br />
Diese Klasse stellt ein GUI-Model für Attributmengen dar.<br />
• AttributeValueCountTableModel<br />
Diese Klasse stellt ein GUI-Model für ” n(A, R)“-Metadaten dar.<br />
• MetaDataTableModel<br />
Diese Klasse stellt ein GUI-Model für Metadaten dar.<br />
• TableTableModel<br />
Diese Klasse stellt ein GUI-Model für Tabellen <strong>und</strong> Indexe dar.<br />
***<br />
• AttributeSetList<br />
Diese Klasse stellt ein GUI-Element für Attributmengen dar.<br />
• MetaDataTable<br />
Diese Klasse stellt ein GUI-Element für die Metadaten einer Relation dar.<br />
• TableTable<br />
Diese Klasse stellt ein GUI-Element für Tabellen <strong>und</strong> Indexe dar.
7.2. GRAFISCHE BENUTZERSCHNITTSTELLE GUI 95<br />
Abbildung 7.4: Metadatenfenster des SQL-Optimierers<br />
***<br />
• TreeTextArea<br />
Mit dieser Klasse wird ein Container für die jeweiligen Anzeigen der Baumdarstellungen<br />
geboten. Er ist nicht editierbar <strong>und</strong> kann zusätzlich dazu in<br />
einen eigenen Dialog vergrößert werden.<br />
• TreeCostTextArea<br />
Diese Klasse bietet als modifizierte TreeTextArea zusätzlich noch die Anzeige<br />
der Gesamtkosten des dargestellten Baumes.<br />
***<br />
• AttributeAddDialog<br />
Diese Klasse stellt ein GUI-Element zur Eingabe von Attributmengen dar.<br />
• AttributeEditDialog<br />
Diese Klasse stellt einen Dialog zur Bearbeitung von Attributmengen dar.<br />
• AttributeValueCountEditDialog<br />
Diese Klasse stellt einen Dialog zur Bearbeitung von ” n(A, R)“-Metadaten<br />
dar.
96 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
• IndexAddDialog<br />
Ein Unterdialog von TableEditDialog zum Hinzufügen eines Indexes für<br />
eine Tabelle.<br />
• IndexDataDialog<br />
Ein Unterdialog von TableEditDialog zum Bearbeiten der Daten eines<br />
Indexes.<br />
• InfoDialog<br />
Diese Klasse wird aufgerufen, wenn der Benutzer den Info-Button drückt.<br />
Sie beinhaltet online Informationen zur Benutzung des Programmes.<br />
• MetaDataDialog<br />
Diese Klasse stellt einen Dialog zur Bearbeitung von Metadaten dar.<br />
• SearchDialog<br />
In diesem Programmabschnitt werden die gewünschten Suchparameter übergeben,<br />
so daß je nach Wunsch ein CPU-Constraint-System oder ein I/O-<br />
Constraint-System simuliert wird.<br />
• TableAddDialog<br />
Ein Unterdialog von TableEditDialog zum Hinzufügen einer Tabelle.<br />
• TableDataDialog<br />
Ein Unterdialog von TableEditDialog zum Bearbeiten der Daten einer<br />
Tabelle.<br />
• TableEditDialog<br />
Diese Klasse stellt einen Dialog zur Bearbeitung von Tabellen <strong>und</strong> Indexen<br />
dar.<br />
7.3 Der Relationale Anfrageparser<br />
7.3.1 de.unihannover.dbs.sopt.sqlp<br />
Die in SQL bzw. in relationaler Form eingegebene Anfrage muß anfangs so verarbeitet<br />
werden, daß der nachfolgende Optimierer sie weiterbenutzen kann. Für<br />
diesen speziellen Zweck wurde ein Parser programmiert, der dem Benutzer die<br />
grafische Eingabe ermöglichen soll <strong>und</strong> dessen Input dann in die passende Datenstruktur<br />
umwandelt.<br />
In der vorliegenden Realisierung werden bislang SPJ-Anfragen vollständig verarbeitet.<br />
Der komplette Sprachumfang von SQL konnte aus Zeitgründen nicht<br />
h<strong>und</strong>ertprozentig simuliert werden, dafür wird die gesamte Ausdrucksstärke der<br />
Relationalen Algebra verstanden. Darüberhinaus können durch den modularen
7.3. DER RELATIONALE ANFRAGEPARSER 97<br />
Aufbau des Übersetzers spätere Erweiterungen der Anfragesprache leicht implementiert<br />
werden.<br />
Folgendes Beispiel soll die pseudo SQL-Anfragemöglichkeit des Anfrageparsers<br />
darstellen:<br />
SELECT S.Semester<br />
FROM ((S PRODUCT H) PRODUCT V) PRODUCT P<br />
WHERE P.Name = ’Sokrates’ AND<br />
V.VorlNr = H.VorlNr AND<br />
V.gelesenVon = P.Name AND<br />
H.MatrNr = S.MatrNr<br />
Der Befehl PRODUCT mußte zusätzlich eingeführt werden, da standardmäßig eine<br />
mit Komma getrennte Eingabe von Relationen sofort in Joins umgesetzt wird.<br />
Um dabei komform mit der SQL-Sprache zu bleiben <strong>und</strong> zusätzlich dazu den<br />
Optimierer die Möglichkeit zu geben, auf Produkten arbeiten zu können, wurde<br />
das genannte Kürzel eingeführt.<br />
Für den relationalen Anfrageparser muß die Anfrage wie folgt aussehen:<br />
(PROJECTION<br />
S.Semester<br />
(SELECTION<br />
(AND P.Name=’Sokrates’ V.VorlNr=H.VorlNr<br />
V.gelesenVon=P.Name H.MatrNr=S.MatrNr)<br />
(PRODUCT (PRODUCT (PRODUCT S H) V) P)))<br />
Dabei ist noch zu erwähnen, daß die relationale Anfragsprache in etwa der Syntax<br />
der Programmiersprache Scheme entspricht.<br />
Interfaces:<br />
• AlgebraicParser<br />
Dieses Interface definiert eine allgemeine Schnittstelle für Parser, die als<br />
Zeichenketten kodierte algebraische Bäume in das benötigte Datenmodell<br />
übersetzt.<br />
Klassen:<br />
• RelationalParser<br />
Diese Klasse ist eine Implementation von AlgebraicParser. Er benutzt<br />
eine Scheme-artige Syntax, die das komplette Datenmodell unterstützt.<br />
• SQLParser<br />
Diese Klassse ist eine Implementation von AlgebraicParser. Er benutzt<br />
eine SQL-artige Syntax, die nur einen Teil des Datenmodells unterstützt.
98 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
Exception:<br />
• ParseException<br />
Signalisiert, daß ein Fehler während des Parsens aufgetreten ist.<br />
7.4 Datenstruktur des Anfrageoptimierers<br />
Nachdem der Anfrageparser die Eingabe zerlegt hat, müssen jetzt die benötigten<br />
Objekte erzeugt werden, mit denen man die Anfrageoptimierung simulieren<br />
möchte. Aus diesem Gr<strong>und</strong> beinhaltet dieses Package Klassen <strong>und</strong> Interfaces zur<br />
Modellierung der Datenstruktur algebraischer Bäume (siehe Abbildung 7.5).<br />
7.4.1 de.unihannover.dbs.sopt.struc.alg<br />
Interfaces:<br />
• Relation<br />
Dieses Interface definiert einen Knoten im algebraischen Baum, wie eine<br />
Table, eine Selection oder auch eine Union.<br />
• CommutativeRelation<br />
Dieses Interface erbt von Relation <strong>und</strong> beschreibt kommutative Relationen.<br />
• Condition<br />
Dieses Interface beschreibt abstrakte Bedingungen.<br />
• AtomicCondition.Theta<br />
Dieses Interface definiert mathematische Prädikate, wie , ≤, ≥.<br />
Klassen:<br />
• Attribute<br />
Diese Klasse repräsentiert ein einzelnes Attribut.<br />
• AttributeSet<br />
Diese Klasse repräsentiert eine Menge von Attributen, wie zum Beispiel das<br />
Schema einer Relation oder die Attributmenge einer Condition.<br />
• AlgebraicTree<br />
Diese Klasse stellt den zu optimierenden algebraischen Baum dar <strong>und</strong> kann<br />
als Kopfzelle der Baumdatenstruktur verstanden werden.<br />
***
7.4. DATENSTRUKTUR DES ANFRAGEOPTIMIERERS 99<br />
• Table<br />
Diese Implementation einer Relation repräsentiert als Blatt der Baumdatenstruktur<br />
eine Tabelle innerhalb der Datenbank.<br />
***<br />
• Selection<br />
Diese Implementation einer Relation repräsentiert die Selektion einer Relation<br />
nach einer bestimmten Condition.<br />
• Projection<br />
Diese Implementation einer Relation repräsentiert die Projektion einer<br />
Relation mit einem bestimmten AttributeSet.<br />
• Join<br />
Diese Implementation einer Relation repräsentiert den Verb<strong>und</strong> zweier<br />
Relations unter einer bestimmten Condition.<br />
***<br />
• Difference<br />
Diese Implementation einer Relation repräsentiert die Differenz zweier<br />
Relations.<br />
• Intersection<br />
Diese Implementation einer Relation repräsentiert den Schnitt zweier Relations.<br />
• Product<br />
Diese Implementation einer Relation repräsentiert das Produkt zweier<br />
Relations.<br />
• Union<br />
Diese Implementation einer Relation repräsentiert die Vereinigung zweier<br />
Relations.<br />
***<br />
• AbstractCondition<br />
Diese abstrakte Implementierung von Condition bietet insbesondere eine<br />
Methode zur Überprüfung, ob das Objekt eine Joinbedingung ist.<br />
• AtomicCondition<br />
Diese Implementierung von Condition repräsentiert die atomaren Bedingungen,<br />
wie zum Beispiel ” R1.A = 4“ oder ” R1.c = R2.c“.<br />
• AND<br />
Diese Implementierung von Condition beschreibt das logische Und einer
100 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
PhysicalRelation<br />
Package de.unihannover.dbs.sopt.struc<br />
struc.phys struc.alg<br />
Appraisable PhysicalTree<br />
PhysicalTable<br />
AppraisableProjection<br />
AppraisableSelection<br />
AppraisableJoin<br />
struc.alg<br />
AttributeCondition<br />
Selection<br />
Condition<br />
Join<br />
Difference<br />
Table<br />
Projection<br />
Selection<br />
Join<br />
AND OR NOT AtomicConditionTheta<br />
Relation<br />
AttributeSet<br />
AtomicCondition Attribute<br />
AlgebraicTree<br />
Relation<br />
CommutativeRelation<br />
Projection<br />
Abbildung 7.5: Abhängigkeitsgraph der Klassen aus sopt.struc<br />
Product<br />
Union<br />
Intersection<br />
erbt nach<br />
benutzt<br />
Interface<br />
Klassen<br />
Packagegrenzen
7.4. DATENSTRUKTUR DES ANFRAGEOPTIMIERERS 101<br />
Liste von Conditions.<br />
• OR<br />
Diese Implementierung von Condition beschreibt das logische Oder einer<br />
Liste von Conditions.<br />
• NOT<br />
Diese Implementierung von Condition beschreibt das logische Nicht einer<br />
Condition.<br />
7.4.2 de.unihannover.dbs.sopt.struc.alg.rules<br />
Dieses Paket beinhaltet Strukturen <strong>und</strong> Implementierungen für Regeln zur Optimierung<br />
algebraischer Bäume (siehe Abbildung 7.5).<br />
Interfaces:<br />
• Rule<br />
Dieses Interface definiert eine Regel zur Optimierung eines algebraischen<br />
Baumes.<br />
Klassen:<br />
• AbstractRule<br />
Diese abstrakte Implementation von Rule enthält zusätzliche Methoden zur<br />
Vereinfachung lokaler Optimierungen.<br />
Zusätzlich dazu findet man in diesem Paket die Implementierungen, der in Kapitel<br />
2.1 vorgestellten Ersetzungsregeln a) - t) wie z. B. ARule.<br />
***<br />
7.4.3 de.unihannover.dbs.sopt.struc.phys<br />
Dieses Paket erweitert die Datenstruktur algebraischer Bäume um physische Operatoren<br />
zum Datenmodell für physische Bäume (siehe Abbildung 7.6).<br />
Interfaces:<br />
• Appraisable 2<br />
Beschreibt ein Element innerhalb der Kostenoptimierung. Dabei bietet es<br />
die Möglichkeit sowohl die lokalen Kosten des Knotens, als auch rekursiv<br />
die Kosten des gesamten Teilbaumes zur erhalten.<br />
2 Wann immer eine abstrakte Klasse in Zukunft einen Kostenwert beinhalten soll, wird er<br />
als appraisable (bewertbar) definiert.
102 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
• PhysicalRelation<br />
Dieses Interface erweitert Relation um physische Eigenschaften (wie |R|,<br />
Index(R) oder n(A, R)) <strong>und</strong> Berechenbarkeit (Appraisable).<br />
Klassen:<br />
• MetaData<br />
Diese Klasse beschreibt die benötigten Metadaten einer Relationen, als ” Ersatz“<br />
für das Datenbank-Management-System.<br />
• PhysicalTree<br />
Diese Klasse erweitert den AlgebraicTree zum physischen Bäume.<br />
• Transformer<br />
Diese Klasse transformiert einen algebraischen Baum (AlgebraicTree) in<br />
einen physischen Prototypen (PhysicalTree). Sie beinhaltet somit die physische<br />
Heuristik zur Optimierung eines Anfragebaumes.<br />
***<br />
• PhysicalTable<br />
Diese Klasse erweitert Table um physische Eigenschaften <strong>und</strong> Berechenbarkeit<br />
(Appraisable).<br />
***<br />
• AppraisableJoin<br />
Diese physische Erweiterung einer algebraischen Joins implementiert als<br />
abstrakte Klasse einen Großteil der Eigenschaften eines physischen Joins.<br />
• AppraisableProjection<br />
Diese physische Erweiterung einer algebraischen Projection implementiert<br />
als abstrakte Klasse einen Großteil der Eigenschaften einer physischen Projektion.<br />
• AppraisableSelection<br />
Diese physische Erweiterung einer algebraischen Selection implementiert<br />
als abstrakte Klasse einen Großteil der Eigenschaften einer physischen Selektion.<br />
***<br />
• IndexSelection<br />
Diese Klasse stellt die Selektion mit Hilfe eines Indexes dar.<br />
• RelSelection<br />
Diese Klasse beschreibt die relationale Selektion.
7.4. DATENSTRUKTUR DES ANFRAGEOPTIMIERERS 103<br />
Join<br />
Projection<br />
Selection<br />
Table<br />
AlgebraicTree<br />
CommutativeRelation<br />
PhysicalRelation<br />
Appraisable<br />
Relation<br />
Package de.unihannover.dbs.sopt.struc.phys<br />
AppraisableJoin<br />
AppraisableProjection<br />
AppraisableSelection<br />
PreExecuted<br />
PhysicalTable<br />
AntiJoin<br />
AntiSemiJoin<br />
HashJoin<br />
IndexJoin<br />
LeftOuterJoin<br />
MergeJoin<br />
NestedLoopJoin<br />
RightOuterJoin<br />
SemiJoin<br />
IndexDupProjection<br />
NestedDupProjection<br />
SortedDupProjection<br />
IndexSelection<br />
RelSelection<br />
Hash<br />
Pipeline<br />
Sort<br />
Tree<br />
Transformer<br />
PhysicalTree MetaData<br />
LeftRightOuterJoin<br />
IndexIndexJoin<br />
RelIndexJoin<br />
Abbildung 7.6: Abhängigkeitsgraph der Klassen aus sopt.struc.phys<br />
Interfaces<br />
Klassen<br />
erbt von
104 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
• IndexDupProjection<br />
Diese Klasse implementiert eine Index-Projektion.<br />
• NestedDupProjection<br />
Diese Klasse beinhaltet die Beschreibung einer Projektion mit Hilfe einer<br />
einfachen Duplikateliminierung.<br />
• SortedDupProjection<br />
Diese Klasse stellt die Projektion mit Hilfe einer Sortierung dar.<br />
Zusätzlich dazu findet man in diesem Paket die Implementierungen, der in Kapitel<br />
3 vorgestellten Verb<strong>und</strong>operatoren wie z. B. HashJoin.<br />
Als nächstes folgen die Optimierungsfunktionen, die bei der physischen bzw. kostenbasierten<br />
Optimierung benötigt werden. Da sie vor einem SPJ-Operator eingefügt<br />
werden, wurden sie als PreExecuted implementiert:<br />
***<br />
• PreExecuted<br />
Diese besondere physische Relation beschreibt Operationen wie Pipelines,<br />
Sortierungen oder Indexierungen.<br />
• Hash<br />
Diese Klasse beschreibt den Aufbau eines Hashes.<br />
• Sort<br />
Diese Klasse beschreibt die Sortierung einer Relation.<br />
• Tree<br />
Diese Klasse beschreibt den Aufbau eines sortierten Indexbaumes über eine<br />
Relation.<br />
• Pipeline<br />
Diese Klasse stellt ein Pipelining zwischen zwei Operationen dar.<br />
7.4.4 de.unihannover.dbs.sopt.struc.search<br />
Dieses Paket besitzt Klassen <strong>und</strong> Interfaces, die die Suchstrategie zur Gewinnung<br />
des optimalen Anfragbaumes darstellen sollen. Man kann sogar der Combined-<br />
Optimizer Klasse die Reihenfolge von verschiedenen Optimierungsmethoden vorgeben,<br />
die dann nacheinander oder parallel ausgeführt werden.<br />
Interfaces:<br />
• CostBasedOptimizer<br />
Dieses Interface beschreibt einen Kostenbasierten Optimierer.
7.5. DIE TESTSCHNITTSTELLE 105<br />
Klassen:<br />
• CombinedOptimizer<br />
Ein kostenbasierter Optimierer, der eine Folge anderer Optimierer nacheinander<br />
ausführt.<br />
• Builder<br />
Fügt lokal eine Sortierung, Indexierung bzw. Partitionierung zur Verbesserung<br />
ein, indem er die ursprünglichen mit den neuen Kosten vergleicht.<br />
(Die Klasse Builder beschreibt, den in Kapitel 6 vorgestellten Optimierer.)<br />
• Indexer<br />
Fügt anfangs für jede Tabelle Trees ein, um sie danach einzeln - je nach<br />
Nutzen - wieder zu entfernen.<br />
7.5 Die Testschnittstelle<br />
7.5.1 de.unihannover.dbs.sopt.test<br />
Die Testschnittstelle beinhaltet alle zusätzlichen Klassen, die dem Benutzer das<br />
Simulieren der Anfragen ermöglichen.<br />
Klassen:<br />
• SQLOpt<br />
Die Startklasse des SQL- bzw. Relationalen-Optimierers.<br />
7.6 Benutzerhandbuch<br />
Auf der beigelegten CD-ROM befinden sich neben einer digitalen Kopie der Diplomarbeit<br />
auch die Source-Dateien mit der dazugehörigen Dokumentation zum<br />
SQL-Optimierer.<br />
7.6.1 Starten des SQL-Optimierers<br />
Der SQL-Optimierer benötigt ein installiertes Java Runtime Environment ab der<br />
Version 1.4.1. Zum Aufruf des Programmes muß nur im SQL-Optimierer Verzeichnis<br />
folgender Startbefehl eingegeben werden:<br />
java -jar SQLOpt für den SQL-Parser<br />
java -jar RelOpt für den Relationalen-Parser
106 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS<br />
Abbildung 7.7: Startfenster des SQL-Optimierers<br />
Danach öffnet sich automatisch die Eingabemaske, in der man links oben die Anfrage<br />
eingeben kann. Nach Betätigen der einzelnen Buttons, die sich im rechten<br />
Fenster befinden, werden die jeweiligen Optimierungsschritte ausgeführt. Je nach<br />
Wahl wird entweder nur eine 1:1-Übersetzung, Algebraische-, Physische- oder eine<br />
Kostenbasierte Optimierung durchgeführt. Da die Optimierungsschritte voneinander<br />
abhängig sind, werden benötigte Optimierungen automatisch ausgeführt,<br />
so daß ein sofortiges Starten der Kostenbasierten Optimierung auch die anderen<br />
Methoden aufruft.<br />
Nicht zu vergessen sind die Relationen, die man nach dem Drücken des dazugehörigen<br />
Knopfes in einer Eingabemaske definieren kann. Im Metadatenfenster<br />
können daraufhin die benötigten Zusatzinformationen festegelegt werden. Schließlich<br />
vervollständigt das Suchparameterfenster den Optimierer, in dem es dem Be-
7.6. BENUTZERHANDBUCH 107<br />
nutzer die Simulation eines I/O-Constraint oder CPU-Constraint-Systems durch<br />
die Wahl eines sinnvollen Proportionalitätsfaktors ermöglicht.
108 KAPITEL 7. DIE IMPLEMENTIERUNG DES SQL-OPTIMIERERS
Kapitel 8<br />
Ausblick<br />
Ich habe keine besondere Begabung, sondern<br />
bin nur leidenschaftlich neugierig.<br />
Albert Einstein<br />
• Entwicklung eines leistungsfähigeren Kostenmodells<br />
Nach der Entwicklung dieser Diplomarbeit wurde eines sofort klar; das<br />
gewählte Kostenmodell, welches auf der Dokotorarbeit von Utesch [54] basierte,<br />
hätte gr<strong>und</strong>sätzlich neu entwickelt werden müssen. Nicht nur, daß<br />
manche Formeln, wie sie in der Literatur wiederzufinden sind, kein richtiges<br />
Ergebnis liefern, sie werden auch nach der Korrektur der Funktionen den<br />
Ansprüchen, die man an ein sinnvolles Kostenmodell stellt, nicht gerecht.<br />
Das Utesch-Modell entspricht eigentlich nur einem Input-Kostenmodell, da<br />
es keinerlei Output-Kosten betrachtet. Zwar kann man annehmen, daß jegliche<br />
Outputkosten einer Operation gleich der Inputkosten des nächsten<br />
Knotens sind <strong>und</strong> deshalb nur einmal berechnet werden müssen (der letzte<br />
Knoten kostet bei allen Anfragebäumen immer gleich), aber durch die vielen<br />
verschiedenen Variablen wäre eine feinere Auflösung des Kostenmodells<br />
in Input-Kosten, Output-Kosten <strong>und</strong> CPU-Kosten besser gewesen.<br />
Zusätzlich dazu fallen die CPU-Kosten in keinem der bislang berechneten<br />
Fälle ins Gewicht, da sie in Relation zu den I-Kosten nur einen sehr kleinen<br />
Bruchteil ausmachen <strong>und</strong> somit für den Entscheidungsprozeß nicht wichtig<br />
sind. Deshalb sollte man, bei einer Weiterführung dieses Themas das<br />
vorliegende Kostenmodel an die jeweiligen neuen Anforderungen anpassen.<br />
• Vergleich des entwickelten Optimierers mit den existierenden Implementierungen<br />
(Oracle, MySQL, DB2 usw.)<br />
Als nächsten Punkt muß man die etwas knapp bemessene Zeit ansprechen,<br />
die sich im Laufe der Diplomarbeit bewahrheitete. Somit wurden Erweiterungen,<br />
die nicht Bestandteil der Aufgabenstellung der Diplomarbeit<br />
waren, größtenteils weggelassen. Darunter fiel die erhoffte Leistungsanalyse<br />
des SQL-Optimierers mit Hilfe des in Kapitel A vorgestellten Oracle-<br />
109
110 KAPITEL 8. AUSBLICK<br />
Optimierers.<br />
In Java hat man durch die JDBC-Schnittstelle die Möglichkeit, Anfragen<br />
an die Datenbank zu stellen, ohne daß sie weiter optimiert werden. Somit<br />
wäre eine interessante Analyse der verschiedenen Optimierungstechniken<br />
möglich gewesen, da man auch ein von Oracle genutztes Kostenmodell als<br />
Vergleichsoperator besitzen würde.<br />
• Untersuchung der neu entwickelten Heuristiken mit verschiedenen<br />
Datentypen<br />
Bei der Implementierung wurde das gesamte Potential der algebraischen<br />
- wie auch der physischen Heuristik - nicht vollständig ausgelotet. Nach<br />
ein paar Bespielen <strong>und</strong> dem Test der korrekten Optimierung wurde eine<br />
spezielle Analyse der verschiedenen Prototypen nicht mehr angegangen.<br />
Dieses Gebiet bietet deshalb noch viel Freiraum für Forschung. Zwar sind<br />
die algebraischen Methoden begrenzt, aber dafür kann durch die physischen<br />
Heuristiken noch viel an zusätzlicher Optimierungszeit eingespart werden.<br />
• Erweiterung des SQL- bzw. des Relationalen Parsers<br />
Durch den modularen Aufbau der geforderten Implementierung des SQL-<br />
Optimierers, ist ein Ausbau des Simulators auf spezielle Gebiete leicht<br />
möglich. Dafür kann die Anfragesprache auf den kompletten Sprachumfang<br />
von SQL erweitert werden. Im Gegensatz dazu bietet der Relationale<br />
Parser schon jetzt viele Möglichkeiten, verschachtelte Anfragen zu stellen.<br />
Abschließend sollte man erwähnen, daß ein Teil der Literatur, die für diese Diplomarbeit<br />
genutzt wurde, lückenhaft war. Aus diesem Gr<strong>und</strong> mußte vieles durch<br />
eigene Forschung wieder ausgeglichen werden. Darunter die genauen Angaben der<br />
verschiedenen Iteratoren <strong>und</strong> die Heuristiken, deren Ausarbeitung bislang in keinem<br />
Buch in dieser Form zu finden ist. Dies war zwingend notwendig geworden,<br />
da sich sonst diese Diplomarbeit nicht hätte realisieren lassen können. Somit<br />
richtete sich ein Großteil der Bemühungen auf die Schaffung neuer Erkenntnisse.<br />
Gerade deswegen wäre die weitere Untersuchung dieses Themas in Richtung einer<br />
Dissertation ein lohnendes Ziel.
Anhang A<br />
Der Oracle Optimierer<br />
Freude am Schauen <strong>und</strong> Begreifen<br />
ist die schönste Gabe der Natur.<br />
Albert Einstein<br />
Der Oracle-Optimierer 1 als Komponente des Datenbankservers hat die Aufgabe,<br />
für einen gegebenen SQL-Befehl den möglichst besten (d.h. kostengünstigsten,<br />
schnellsten) Zugriffsplan zu ermitteln. Er ist für die Umsetzung der Mengenoperationen,<br />
wie sie in SQL-Befehlen einer Anwendung formuliert sind, in konkrete<br />
Zugriffsoperationen verantwortlich.<br />
Weil jede relationale Operation im Prinzip in verschiedene Zugriffsspläne umgesetzt<br />
werden kann, diese jedoch z. B. abhängig von der Menge der Daten zu<br />
unterschiedlichen Laufzeiten führen können, ist die Leistungsfähigkeit des Optimierers<br />
für jedes relationale Datenbanksystem von entscheidender Bedeutung.<br />
Der in Oracle implementierte Optimierer kann nach den folgenden drei Methoden<br />
arbeiten:<br />
• Regelmethode (rule based optimizer)<br />
• Statistische Methode (cost base optimizer)<br />
• Statistische Methode mit Hinweisen (cost based optimizer with hints)<br />
A.1 Rule based optimizer<br />
Die Regelmethode ist die Standardmethode, die schon zur Zeit von Oracle 6<br />
eingeführt wurde <strong>und</strong> seitdem ein fester Bestandteil des Optimierers ist. Die<br />
Optimierung erfolgt dabei nach einem festen Regelwerk, wofür Informationen<br />
aus dem Data Dictionary (z. B. Informationen über indizierte Spalten) zugr<strong>und</strong>e<br />
gelegt werden.<br />
1 Wir betrachten hier den Oracle 9i-R2 Optimierer.<br />
111
112 ANHANG A. DER ORACLE OPTIMIERER<br />
In der folgenden Tabelle ist das Bewertungsschema, das sogenannte ” Rankingschema“,<br />
dargestellt, auf dessen Basis der regelbasierte Optimierer die SQL-<br />
Befehle oder ihre Prädikate klassifiziert.<br />
Das am höchsten klassifizierte Prädikat <strong>und</strong> der Index im Zusammenhang mit<br />
diesem Prädikat steuern dann die Zugriffe auf die betreffende Tabelle. Natürlich<br />
kann man als letzte sinnvolle Methode einen kompletten Tabellendurchlauf durchführen<br />
( ” Full Table Scan“):<br />
1 Ein Datensatz über rowid<br />
2 Ein Datensatz über Cluster-Join = Konstante<br />
3 Ein Datensatz über Hash-Cluster mit eindeutigem Index = Konstante<br />
4 Ein Datensatz über eindeutigen Index = Konstante<br />
5 Cluster Join auf geclusterten Tabellen<br />
6 Hash-Cluster-Schlüssel<br />
7 Cluster-Index auf eine Tabelle<br />
8 Zusammengesetzter Index = Konstante<br />
9 Einfacher Index = Konstante<br />
10 Index range scan mit festen Grenzen<br />
11 Index range scan ohne festen Grenzen<br />
12 sort / merge Join<br />
13 Max / Min auf Indexspalten<br />
14 Order by mit vollständigem Index<br />
15 Full Table Scan<br />
Tabelle A.1: Rule based optimizer<br />
A.2 Cost based optimizer<br />
Die statistische Methode wird in den Fällen gewählt, in denen sich die Zugriffstrukturen<br />
nicht als trivial herausstellen. Sie bedingt aber das Vorliegen einer<br />
aktuellen Tabellenstatistik, die explizit aufgestellt werden muß.<br />
Damit die Statistiken den laufenden Betrieb nicht behindern, werden sie typischerweise<br />
nur auf expliziten Wunsch des DB-Administrators aktualisiert (in<br />
Oracle mit dem Kommando analyse).<br />
Im einzelnen umfaßt diese Optimierungsmethode folgende drei Unterpunkte:<br />
• Generierung aller möglichen Zugriffspfade<br />
• Berechnung der Kosten für jeden möglichen Zugriffspfad durch Einbeziehung<br />
der gespeicherten Statistiken. Die Kosten beziffern die zu erwartende<br />
Nutzung von Ressourcen, z. B. I/O-Operationen, Hauptspeicherbedarf <strong>und</strong><br />
die CPU-Zeit.<br />
• Der Plan mit den geringsten Kosten wird ausgewählt.
A.3. COST BASED OPTIMIZER MIT HINTS 113<br />
Die Methoden können sowohl global für die gesamte Datenbank gewählt werden<br />
als auch auf Sessionebene - durch ein alter session-Statement. Auf Befehlsebene<br />
besteht die Möglichkeit der Nutzung von Hinweisen (hints).<br />
Problematisch kann ein Fehlen oder das Alter einer Statistik sein. Falls keine<br />
vorhanden ist, wird nur regelbasiert optimiert. Bei veralteten Statistiken wird<br />
weiterhin nach ihnen ” optimiert“, was zu schlechten Zugriffsplänen führen kann.<br />
A.3 Cost based optimizer mit hints<br />
Zur manuellen Optimierung werden zusätzlich Hints verwendet. Wenn der Ausführungspfad<br />
eines Kommandos analysiert ist <strong>und</strong> der speziell in der gewünschten<br />
Situation optimale Zugriffspfad durch Tests ermittelt ist, bieten Hints die<br />
Möglichkeit, diesen Zugriffspfad für dieses Statement zu fordern.<br />
werden.<br />
Oracle-Hints in SQL-Statements beeinträchtigen die Portabilität nicht, da sie von<br />
anderen SQL-Interpretern als Kommentare interpretiert werden.<br />
Die am meisten genutzten Hints sind:<br />
1) cost - fordert den Statistik-Optimierer für das Statement<br />
2) rule - fordert den regelbasierten Optimierer<br />
3) full [(table name)] - fordert einen Full Table Scan (auf eine bestimmte Tabelle)<br />
4) rowid [(table name)] - fordert den Zugriff über rowid, falls möglich<br />
5) index [(table name [index name])] - fordert den indizierten Zugriff, ggf. über<br />
den angegebenen Index<br />
6) ordered - fordert einen Join in der Reihenfolge der Tabelle in der from-Klausel<br />
(Umkehrung des regelbasierten Modus, also von rechts nach links)<br />
7) use nl, use merge, use hash - fordert explizit nested loop-, sort/merge- oder<br />
Hash Join<br />
8) star transformation - fordert die Berücksichtigung einer Star-Transformation<br />
A.4 Statistiken<br />
Tabelle A.2: Cost based optimizer mit hints<br />
Das Kostenmodell des kostenbasierten Optimierers kann aber nur dann vernünftig<br />
funktionieren, wenn entsprechende Statistikdaten über die Datenbank zur<br />
Verfügung stehen. Diese werden im allgemeinen weder automatisch erstellt noch<br />
bei Datenbankänderungen aktualisiert.
114 ANHANG A. DER ORACLE OPTIMIERER<br />
Bei Oracle muß der Datenbankadministrator die Generierung selbständig anstoßen.<br />
Dazu bedient er sich des Befehls:<br />
analyze table . . . compute statistics for table;<br />
Auf diese Art werden dann alle Datenbankstrukturen (Relationen <strong>und</strong> Indexe)<br />
analysiert. Es ist sinnvoll, diese Analysen periodisch zu wiederholen, da der Optimierer<br />
nur dann vernünftig arbeitet, wenn die Daten aktuell bleiben.<br />
Diese Daten werden dann in die dafür vorbereitete Relation geschrieben; dem<br />
sogenannten Data Dictionary, in dem auch Schemainformationen verwaltet <strong>und</strong><br />
gespeichert werden.<br />
Falls man dennoch schlechte Ausführungspläne bekommt, kann man sie sich auch<br />
anzeigen lassen. Oracle 9i bietet dafür den Befehl<br />
explain plan for . . .<br />
an, der einem zusätzlich die Kosten angibt. Dabei bietet Oracle auch eine graphische<br />
Benutzerschnittstelle an, in der Auswertungspläne als Anfragebäume dargestellt<br />
werden.<br />
A.5 Standardeinstellungen des Optimierers<br />
Die systemweite Voreinstellung für den Oracle-Optimierer ist der Modus CHOOSE.<br />
Dieser Modus veranlaßt den Optimierer, für eine SQL-Anweisung zwischen dem<br />
regelbasierten <strong>und</strong> kostenbasierten Ansatz zu wählen. Enthält das Data Dictionary<br />
statistische Daten für mindestens eine der in der Anfrage vorkommenden<br />
Tabellen, auf die von einer Anfrage zugegriffenen wird, so verwendet der Optimierer<br />
den kostenbasierten Ansatz <strong>und</strong> optimiert mit dem Ziel des höchsten<br />
Datendurchsatzes (ALL ROWS). Enthält das Data Dictionary keine statistischen<br />
Daten für die Anfragetabellen, verwendet der Optimierer den regelbasierten Ansatz<br />
RULE.<br />
Dabei kann der Optimierungsmodus vom Benutzer mit folgenden SQL-Befehlen<br />
gewählt werden:<br />
alter session set optimizer goal = ALL ROWS | FIRST ROW | CHOOSE | RULE<br />
Wird explizit der Modus RULE angegeben, erfolgt stets eine regelbasierte Optimierung.<br />
Vorhandene Statistiken werden vom Optimierer nicht berücksichtigt.
A.6. INDEXE 115<br />
A.6 Indexe<br />
Indexe sind optionale Zugriffspfade auf Tabellen <strong>und</strong> Cluster. Sie erhöhen die<br />
Bearbeitungsgeschwindigkeit für Anfragen, die z. B. nur einen kleinen Teil der<br />
Datensätze einer Tabelle betreffen oder die mit Hilfe der indexierten Spalten<br />
einen Verb<strong>und</strong> berechnen. Die häufigste Datenstruktur für einen Index ist der<br />
B*-Baum. Daneben gibt es in Oracle Bitmap-Indexe; sie basieren auf invertierten<br />
Bytefolgen der Schlüssel sowie B*-Bäumen <strong>und</strong> Hashtabellen für Cluster.<br />
Indexe sind logisch <strong>und</strong> physikalisch unabhängig von der zugehörigen Tabelle.<br />
Genauer gesagt ist das Erzeugen bzw. Löschen eines Indexes jederzeit ohne Auswirkung<br />
auf die zugehörige Tabelle oder einen anderen Index möglich.<br />
Nach der Erzeugung eines Indexes wird dieser vom Datanbankmanagement automatisch<br />
benutzt <strong>und</strong> bei Änderungen der Tabelle aktualisiert.<br />
Die einfachste Syntax des create index-Kommandos lautet:<br />
create index <br />
on ( . . .)<br />
Schließlich können Indexe einfach mit dem drop index- Kommando gelöscht<br />
werden.
116 ANHANG A. DER ORACLE OPTIMIERER
Anhang B<br />
Das, wobei unsere Berechnungen<br />
versagen, nennen wir Zufall.<br />
Albert Einstein<br />
Konventionen dieser Arbeit<br />
Typographische Konventionen<br />
In dieser Diplomarbeit werden folgende typographische Konventionen verwendet:<br />
• Package-, Klassen- <strong>und</strong> Variablennamen <strong>und</strong> SQL-Befehle werden in<br />
Schreibmaschinenschrift gesetzt.<br />
• Variablen <strong>und</strong> Formeln für die jeweils ein entsprechender Wert eingesetzt<br />
werden muß, werden im mathematischen Modus angegeben.<br />
• Originalbezeichnungen aus dem Englischen wurden so weit wie möglich ins<br />
Deutsche übersetzt. Dort wo es keinen Sinn ergibt, wurde auf eine Neuformulierung<br />
verzichtet.<br />
117
118 ANHANG B. KONVENTIONEN DIESER ARBEIT
Anhang C<br />
Das Einzige, das mir klar ist:<br />
Gott würfelt nicht!<br />
Albert Einstein<br />
Weblinks zu dieser Diplomarbeit<br />
Am Anfang der Diplomarbeit stand die Internetrecherche, die den Einstieg zu<br />
dem vorgebenen Thema sehr vereinfachte, da die meisten Bücher noch nicht lieferbar<br />
waren. Somit waren die nachfolgenden Vorlesungen, Diplomarbeiten <strong>und</strong><br />
Dissertationen Hauptliteratur der Einarbeitungsphase. Später wurden sie meistens<br />
durch gedruckte Literatur f<strong>und</strong>iert. Aus diesem Gr<strong>und</strong> werden hier nur<br />
kurz ein paar deutsche Links aufgeführt, die eine spätere Analyse des Themas<br />
unterstützen könnten.<br />
Vorlesungen:<br />
• http://lwi2.wiwi.uni-frankfurt.de/lehre/vorlesungen/bed/ws0001/<br />
anfrageoptimierung2.<strong>pdf</strong><br />
http://www.wi.uni-trier.de/lehre/Skripte/Vossen/modul15.<strong>pdf</strong><br />
Die Online Version der Vorlesung von Herrn Prof. Dr. Gottfried Vossen.<br />
• http://www.mathematik.uni-marburg.de/~seeger/vor02SSopti2.shtml<br />
Eine komplette Vorlesung mit dazugehöriger Übung von Herrn Prof. Dr. Bernhard Seeger<br />
zum Thema Anfrageoptimierung.<br />
• http://www.upb.de/cs/info-cd/vorlesungen/boettcher/dabas99/dbs99k4n.html<br />
Die Online Version der Vorlesung von Herrn Prof. Stefan Böttcher.<br />
• http://www.db.fmi.uni-passau.de/~kossmann/DPDB98/dynamic.ps<br />
http://www3.informatik.tu-muenchen.de/lehre/SS2001/DBSother-kossmann/<br />
geodb5.<strong>pdf</strong><br />
http://www.db.fmi.uni-passau.de/~kossmann/IMPL99/week9.ps<br />
Die Online Version der Vorlesung von Herrn Prof. Dr. Donald Kossmann über ” Dynamische<br />
Anfrageoptimierung“.<br />
• http://wwwdbis.informatik.uni-kl.de/courses/VDBS/SS2002/<br />
Vorlesungsunterlagen/Kapitel.04.<strong>pdf</strong><br />
Die Online Version der Vorlesung von Herrn Prof. Dr. Theo Härder über ” Datenallokation<br />
in verteilten <strong>und</strong> parallelen DBS“.<br />
119
120 ANHANG C. WEBLINKS ZU DIESER DIPLOMARBEIT<br />
• http://www.informatik.uni-freiburg.de/~dbis/lehre/db-ws0001/<br />
http://www.informatik.uni-freiburg.de/~dbis/lehre/gis-ws9900/folien/<br />
PowerPoint/vDBAnfrage.ppt<br />
Die Online Version der Vorlesung von Herrn Prof. Dr. Georg Lausen über ” Anfrageoptimierung<br />
in verteilten Datenbank-Systemen“.<br />
• http://cis.cs.tu-berlin.de/Lehre/WS-0102/Sonstiges/db-pages/FolienVL/<br />
vl5vl6.<strong>pdf</strong><br />
http://cis.cs.tu-berlin.de/Lehre/WS-0203/Sonstiges/db-pages/FolienVL/<br />
vl06-4.<strong>pdf</strong><br />
Die Online Version der Vorlesung von Herrn Prof. Dr. Herbert Weber.<br />
• http://pi3.informatik.uni-mannheim.de/lehre/vorlesungen/dbImplSkript/<br />
part1.ps.gz<br />
Eine Vorlesung von Herrn Prof. Dr. G. Markoette zur ” Implementierung von Datenbanksystemen“.<br />
• http://www.db.fmi.uni-passau.de/publications/books/DBMSeinf/EIS/<br />
Kapitel8.<strong>pdf</strong><br />
http://www.db.fmi.uni-passau.de/lehre/SS02/VDBMS/Teil3.ppt<br />
Eine 146 seitige Vorlesung von Herrn Prof. Alfons Kemper über Anfrageoptimierung.<br />
• http://www.imn.htwk-leipzig.de/~kudrass/Lehrmaterial/DB2-VL/PDF/<br />
06-Query-Optimierung.<strong>pdf</strong><br />
Die Online Version der Vorlesung von Herrn Prof. Dr. T. Kudraß über Query-Optimierung.<br />
• http://www.dbs.informatik.uni-muenchen.de/~conrad/VFDBS/kap11.4.ps<br />
Eine Online Version der Vorlesung von Herrn Stefan Conrad über Anfragebearbeitung.<br />
• http://www-i5.informatik.rwth-aachen.de/lehrstuhl/lehre/IDB00/<br />
Eine komplette Vorlesung von Herrn Prof. Dr. M. Jarke zum Thema Anfrageoptimierung<br />
<strong>und</strong> Implementierung <strong>Datenbanken</strong>.<br />
• http://www.uni-mannheim.de/i3v/00217110/01701391.htm<br />
Eine Vorlesung von Herrn Prof. Dr. Guido Moerkotte über ” Algorithmen <strong>und</strong> Komplexitätsaussagen<br />
für die Optimierung algebraischer Ausdrücke zur Anfragebearbeitung in<br />
<strong>Datenbanken</strong>“.<br />
• http://www-ia.tu-ilmenau.de/~katrin/WEB DB/SS2002/Architektur/Teil III.<strong>pdf</strong><br />
Eine 60 seitige Vorlesung von Herrn Prof. Dr. M. Reichert zum Thema ” Anfragebearbeitung<br />
<strong>und</strong> -optimierung“ von 2002.<br />
• http://www.inf.uni-konstanz.de/dbis/teaching/ws0102/informationssysteme/<br />
local/K7.<strong>pdf</strong><br />
Eine Vorlesung von Herrn Prof. Dr. Gunter Saake <strong>und</strong> Herrn Prof. Dr. Andreas Heuer<br />
zum Thema ” Optimierung von Anfragen“.<br />
• http://wwwdb.informatik.uni-rostock.de/Lehre/Vorlesungen/DBIII01.html<br />
http://wwwdb.informatik.uni-rostock.de/~hme/lehre/psdump/qproc.ps.gz<br />
Eine Vorlesung von Herrn PD Dr.-Ing. Holger Meyer über Anfragebearbeitung.<br />
• http://www.informatik.uni-ulm.de/dbis/papers/vdb-buch/vdb99 06.<strong>pdf</strong><br />
Die Online Version der 60 seitigen Vorlesung von Herrn Prof. Dr. Peter Dadam über<br />
Anfragebearbeitung.
121<br />
• http://ls6-www.informatik.uni-dortm<strong>und</strong>.de/ir/teaching/lectures/is ws99-00/<br />
folien/folien14.<strong>pdf</strong><br />
Die Online Version der Vorlesung von Herrn Prof. Dr. Joachim Biskup aus der Universität<br />
Dortm<strong>und</strong>.<br />
• http://wwwiti.cs.uni-magdeburg.de/~sattler/dd/dbimpl-6.<strong>pdf</strong><br />
http://wwwiti.cs.uni-magdeburg.de/iti db/lehre/db2/skripte/<br />
optimierung2.ps.gz<br />
Die Online Version der Vorlesung von Herrn Dr.-Ing. Kai-Uwe Sattler aus der Universität<br />
Magdeburg.<br />
• http://www-dbs.inf.ethz.ch/~infosys/folien online/kap8/<br />
Übungen:<br />
Die Online Version der Vorlesung von Herrn von Prof. Dr. H.-J. Schek vom 5.5.1997.<br />
Folgende Übungen wurde zum besseren Verständnis des Themas bearbeitet <strong>und</strong><br />
können somit auch als sinnvolle Ergänzung zu den oben genannten Vorlesungen<br />
gesehen werden.<br />
• http://wwwiti.cs.uni-magdeburg.de/~sattler/dd/uebung6.<strong>pdf</strong><br />
• http://www.mathematik.uni-marburg.de/~beringer/tutorium/Muster3.<strong>pdf</strong><br />
• http://www.informatik.uni-ulm.de/dbis/01/lehre/ss02/ue archimpl/<br />
uebung IV folien.<strong>pdf</strong><br />
• http://www.upb.de/cs/sensen/Ue<strong>Datenbanken</strong>I/<br />
• http://www.informatik.uni-ulm.de/dbis/01/lehre/ss02/ue archimpl/<br />
uebungsblatt IV loesung.<strong>pdf</strong><br />
• http://www.ifis.uni-luebeck.de/lehre/ss98/anfrage/uebung.html<br />
• http://www-ia.tu-ilmenau.de/~katrin/WEB DB/SS2002/Architektur/<br />
Uebungen2.<strong>pdf</strong><br />
• http://www.stephan-brumme.com/studies/basistech.html<br />
• http://www.informatik.tu-cottbus.de/~feyer/Lehre/DB5 02/<br />
Arbeiten:<br />
• http://www.informatik.uni-bonn.de/~tb/Lehre/ws98/sIS++/Ausarbeitung/<br />
heikelson a.<strong>pdf</strong><br />
Eine Seminararbeit von Konstantin Heikelson im WS98/99 mit dem Titel ” Anfrageoptimierung“.<br />
• http://www.ifis.uni-luebeck.de/lehre/ss98/anfrage/<br />
oo anfrageoptimierung folie.ps<br />
Ein Folienvortrag von Carsten Lecon vom 14.5.1998 mit dem Titel ” Anfrageoptimierung<br />
in Objektorientierten <strong>Datenbanken</strong>“.<br />
• http://cis.cs.tu-berlin.de/Dokumente/Papers/1999/Les99b.ps.gz<br />
Ein Artikel von Ulf Leser über ” Globale Anfragebearbeitung mit verteilten <strong>und</strong> heterogenen<br />
Datenquellen“ von 1999.
122 ANHANG C. WEBLINKS ZU DIESER DIPLOMARBEIT<br />
• http://www3.informatik.tu-muenchen.de/lehre/SS2000/ausarbeitung6.<strong>pdf</strong><br />
Eine Seminararbeit von Eduard Scherer über ” Datamining - Knowledge Discovery in<br />
Databases“ vom 6.7.2000.<br />
• http://iaks-www.ira.uka.de/iaks-calmet/papers/DISS.ps<br />
Die Dissertationsarbeit von Peter Kullmann über ” Wissenrepräsentation <strong>und</strong> Anfragebearbeitung<br />
logikbasierten Mediatorumgebung“ vom 6.7.2001.<br />
• http://www.sts.tu-harburg.de/slides/1994/10-94-Kira-O.ps.gz<br />
Die Diplomarbeit von Plamen Kieradjiev über ” Dynamische Optimierung in CPS -<br />
Zwischensprachen“ von 10/1994.<br />
• http://e-lib.informatik.uni-rostock.de/fulltext/1998/pre-diploma/<br />
JonasMichael-1998.ps.gz<br />
Die Studienarbeit von Michael Jonas über ” Entwicklung <strong>und</strong> Test von Verfahren zum<br />
Testen von Anfrageoptimierungen“ von 1998.<br />
• http://www.gis1.bv.tum.de/Forschung/Promotionen/Dokumente/Ziegler 2002.<strong>pdf</strong><br />
Die Dissertation von Matthias Ziegler über die ” Untersuchung geographischer Anfragesprachen<br />
auf der Basis relationaler <strong>und</strong> objektrelationaler DBMS“ vom 15.5.2001.<br />
• http://www.iuw.uni-vechta.de/personal/geoinf/diplom/homberg.ps.gz<br />
Die Diplomarbeit von Stefan Schmitz-Homberg über die ” Integrität von Landkarten in<br />
deduktiven <strong>Datenbanken</strong>“ von 6/1998.<br />
• http://www.zurich.ibm.com/~kju/thesis.ps<br />
Seminararbeit von Klaus Julisch über ” Extensibility and Efficiency of Top-Down-Query-<br />
Optimizers“ vom 9.11.1999<br />
• http://www.ipd.uka.de/~kleinm/arbeiten/diplomarbeit.ps<br />
Michael Kleins Diplomarbeit zum Thema ” Eine Pipelining-Algebra für die effiziente Anfragebearbeitung<br />
im KDD-Prozeß“ vom 30.9.2001.<br />
• http://www.ifi.unizh.ch/ifiadmin/staff/rofrei/DA/DA Arbeiten 2000/<br />
Egli Thomas.<strong>pdf</strong><br />
Diplomarbeit von Thomas Egli über ” Anfragebearbeitung in SINGAPORE“ an der Universität<br />
Zürich vom 3.10.2000.<br />
• http://e-lib.informatik.uni-rostock.de/fulltext/1995/diploma/<br />
LuenebergJens-1995.ps.gz<br />
Die Diplomarbeit von Jens Lüneberg über den ” Entwurf <strong>und</strong> Implementierung eines<br />
algebraischen Optimierers für das verteilte Datenbanksystem HEAD“ vom 30.5.1995.<br />
• http://www.wirsam.de/Wido/VisualXXL/DiplomarbeitWido.<strong>pdf</strong><br />
Die Diplomarbeit von Wido Wirsam über ” VisualXXL einer Grafischen Bedienoberfläche<br />
für den XXL Anfrageoptimierer“vom 4/2001.<br />
Source-Sammlungen:<br />
• http://www.informatik.uni-trier.de/~ley/dbi/dbi96.html<br />
Eine Auflistung von Michael Ley von verschiedenen Quellen zum Thema Anfrageoptimierung.
• http://www.informatik.fernuni-hagen.de/import/pi4/themen.html#SECTIONREF<br />
123<br />
Eine Auflistung von mehreren Arbeiten über das Thema Anfrageoptimierung im Zuge<br />
eines Seminars an der Fern-Universität-Hagen.<br />
• http://wwwdb.informatik.uni-rostock.de/Forschung/HEaD.html<br />
Mehrere Artikel zum Projekt Head an der Universitat Rostock.<br />
• http://www.freissler.at/BooksDE/ComputerInternet/Templ004Page003896de.html<br />
Eine Auflistung von Büchern zum Thema Genetische Anfrageoptimierung.<br />
• http://www.informatik.uni-trier.de/~ley/db/dbimpl/qo.html<br />
Eine Auflistung von Büchern zum Thema Query Optimization.<br />
• http://wwwdb.informatik.uni-rostock.de/Forschung/croque/CROQUE.publi.html<br />
Artikel:<br />
Die Hauptseite des DFG-Projektes CROQUE (Cost- and Rulebased Optimization of<br />
object-oriented Queries). Auf dieser Website findet man viele Diplomarbeiten, Studienarbeiten<br />
<strong>und</strong> Artikel über kostenbasierte <strong>und</strong> regelbasierte Anfrageoptimierung in ojektorientierten<br />
<strong>Datenbanken</strong>.<br />
• http://wwwdb.informatik.uni-rostock.de/~gflach/psdump/uhsalz95.ps.gz<br />
http://wwwdb.informatik.uni-rostock.de/~lubinski/artikel/optimierung.ps<br />
Ein Artikel von Uwe Langer <strong>und</strong> Holger Meyer von 1995 über ” Mehrstufige Anfrageoptimierung<br />
in HEAD“.<br />
• http://www.informatik.uni-stuttgart.de/ipvr/as/lehre/skripte/TRSWS9900/<br />
MIDAS.ps.gz<br />
Ein Folienvortrag von Herrn Prof. Dr. Bernhard Mitschang <strong>und</strong> Michael Jaedicke über<br />
” Parallele Objekt-Relationale DBMS - Prototyp MIDAS“ vom 21.12.1999.<br />
• http://www.ubka.uni-karlsruhe.de/vvv/ira/1994/7/7.text<br />
Ein Artikel zum Thema ” Eine Basis für effiziente Konsistenzprüfung“ von Uwe Herzog<br />
<strong>und</strong> Herrn Prof. Dr. Guido Moerkotte vom 1.12.1994.<br />
• http://www.ifi.unizh.ch/dbtg/Classes/371SS2002/Slides/dwh-12.<strong>pdf</strong><br />
Ein Folienvortrag von Andreas Geppert über ” Anfrageausführung & Leistungssteigerung“<br />
im SS2002.<br />
• http://www6.informatik.uni-erlangen.de/research/artikel.doc.html<br />
Ein Online Artikel von Herrn Prof. Dr. H. Wedekind <strong>und</strong> Dipl.-Inf. W. Lehner zu ” Anfrageoptimierung<br />
in statistischen Datenbanksystemen“.<br />
• http://www.gi-ev.de/informatik/lexikon/inf-lex-semant-abfrageopt.shtml<br />
Ein Online Artikel von Siegfried Bell zum Thema ” Semantische Abfrageoptimierung“<br />
(Auszug aus seinem Buch).<br />
• http://idw-online.de/public/pmid-1288/zeige pm.html<br />
Ein Online Artikel von Dr.rer.pol. Dipl.-Kfm. Ragnwolf Knorr zu ” Anfrageoptimierung<br />
in statistischen Datenbanksystemen“.<br />
• http://www.uni-erlangen.de/docs/FAUWWW/Aktuelles/2002/Publikationen 2002/<br />
uk102/71-72.<strong>pdf</strong><br />
Ein Online Artikel von Dipl.-Kauffrau Gabriele Brambach über ” Anfrageoptimierung in<br />
Datawarehousesystemen“.
124 ANHANG C. WEBLINKS ZU DIESER DIPLOMARBEIT<br />
• http://www-is.informatik.uni-oldenburg.de/publications/242.<strong>pdf</strong><br />
Ein Online Artikel von Marco Graw<strong>und</strong>er zum Thema ” Agentenbasierte adaptive <strong>und</strong><br />
dynamische Anfragebearbeitung in virtuellen Datenbanksystemen“.<br />
• http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/<br />
dnarsqlsg/html/msdn qryoptim.asp<br />
Ein Microsoft Artikel über ” Query Optimization Techniques“ von 1994.<br />
• http://www-dbis.informatik.uni-rostock.de/biber2/biber2teile.ps<br />
Ein Ausschnitt aus dem Buch von Prof. Dr. Günther Saake <strong>und</strong> Prof. Dr. Andreas<br />
Heuer über verschiedene Implementationen von Anfrageoptimierern in z. B. IBM DB2oder<br />
Oracle-Systemen.<br />
• http://wwwmath.uni-muenster.de/u/beckelu/SeminarWS0102/Vortrag1.<strong>pdf</strong><br />
Ein Vortrag von Dr. Ludger Becker über ” Gr<strong>und</strong>legende Verfahren zur Berechnung von<br />
Verb<strong>und</strong>operationen“ vom 29.10.2001.<br />
• http://www.mysql.de/documentation/mysql/bychapter/<br />
manual.de MySQL Optimisation.html<br />
Eine Online Version des Handbuches für die Optimierung von MySQL-<strong>Datenbanken</strong>.<br />
• http://www.ipd.uka.de/~schmitt/DBI/Folien html/node61.html<br />
Ein Vortrag über Anfrageoptimierung von Bethina Schmitt vom 5.11.1996.
Abbildungsverzeichnis<br />
1.1 Strategien zur Anfrageoptimierung . . . . . . . . . . . . . . . . . 1<br />
1.2 Ablauf der Anfrageoptimierung . . . . . . . . . . . . . . . . . . . 2<br />
1.3 Phasen der Optimierung . . . . . . . . . . . . . . . . . . . . . . . 4<br />
1.4 Anfrageraum der Ausführungspläne . . . . . . . . . . . . . . . . . 6<br />
2.1 Vom Anfragebaum zum ” dag“-Tree . . . . . . . . . . . . . . . . . 13<br />
2.2 Eliminierung leerer Teilbäume . . . . . . . . . . . . . . . . . . . . 14<br />
2.3 Vom entarteten zu einem ausgewogenen Anfragebaum . . . . . . . 14<br />
2.4 1:1-übersetzter Anfragebaum . . . . . . . . . . . . . . . . . . . . . 15<br />
2.5 Algebraisch-optimierter Anfragebaum . . . . . . . . . . . . . . . . 16<br />
3.1 Zugriffsmethoden der physischen Optimierung . . . . . . . . . . . 21<br />
3.2 Schematische Darstellung eines Auswertungsplanes . . . . . . . . 23<br />
3.3 Generischer Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . 23<br />
3.4 Projektions-Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . 24<br />
3.5 Selektions-Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . 25<br />
3.6 Selektions-Iterator mit Zugriff über vorhandenen Index . . . . . . 25<br />
3.7 NestedLoop-Iterator . . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
3.8 Relationen-Index-Join-Iterator . . . . . . . . . . . . . . . . . . . . 27<br />
3.9 Index-Index-Join-Iterator . . . . . . . . . . . . . . . . . . . . . . . 28<br />
3.10 Merge-Join-Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . 29<br />
3.11 Hash-Join-Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . 30<br />
3.12 physisch optimierter Anfragebaum . . . . . . . . . . . . . . . . . . 39<br />
4.1 Aufbau eines Kostenmodells . . . . . . . . . . . . . . . . . . . . . 41<br />
4.2 Normalverteilter Stichprobenraum . . . . . . . . . . . . . . . . . . 53<br />
4.3 Equi-Depth-Histogramm . . . . . . . . . . . . . . . . . . . . . . . 54<br />
4.4 Beispiel für die Kardinalitätsabschätzung . . . . . . . . . . . . . . 56<br />
4.5 physisch optimierter Anfragebaum . . . . . . . . . . . . . . . . . . 62<br />
5.1 Suchraum der Ausführungspläne . . . . . . . . . . . . . . . . . . . 67<br />
6.1 Ablauf der Anfrageoptimierung . . . . . . . . . . . . . . . . . . . 74<br />
6.2 Schematische Darstellung der Transformation eines Anfragebaums 77<br />
125
126 ABBILDUNGSVERZEICHNIS<br />
6.3 Beipiel für die Transformation eines Anfragebaums . . . . . . . . 78<br />
6.4 Schematische Darstellung der Generierung eines Anfragebaums . . 79<br />
6.5 mögliche Schritte der Generierung . . . . . . . . . . . . . . . . . . 80<br />
6.6 lokale Form des Prototypen . . . . . . . . . . . . . . . . . . . . . 81<br />
6.7 lokale Umgebung . . . . . . . . . . . . . . . . . . . . . . . . . . . 82<br />
6.8 lokale Umgebung . . . . . . . . . . . . . . . . . . . . . . . . . . . 84<br />
6.9 1:1-übersetzter Anfragebaum . . . . . . . . . . . . . . . . . . . . . 86<br />
6.10 Algebraischer Prototyp . . . . . . . . . . . . . . . . . . . . . . . . 86<br />
6.11 Physischer Prototyp . . . . . . . . . . . . . . . . . . . . . . . . . 87<br />
6.12 Kostenoptimierter Anfragebaum . . . . . . . . . . . . . . . . . . . 90<br />
7.1 Die Packages des SQL-Optimierers . . . . . . . . . . . . . . . . . 92<br />
7.2 Das GUI Package des SQL-Optimierers . . . . . . . . . . . . . . . 93<br />
7.3 Relationenfenster des SQL-Optimierers . . . . . . . . . . . . . . . 94<br />
7.4 Metadatenfenster des SQL-Optimierers . . . . . . . . . . . . . . . 95<br />
7.5 Abhängigkeitsgraph der Klassen aus sopt.struc . . . . . . . . . 100<br />
7.6 Abhängigkeitsgraph der Klassen aus sopt.struc.phys . . . . . . 103<br />
7.7 Startfenster des SQL-Optimierers . . . . . . . . . . . . . . . . . . 106
Tabellenverzeichnis<br />
3.1 Zusammenfassende Darstellung der Mengenoperationen . . . . . . 33<br />
3.2 Algebraische Operatoren <strong>und</strong> die dazugehörigen physischen Implementierungen<br />
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35<br />
4.1 Selektivitätsabschätzung für Prädikate . . . . . . . . . . . . . . . 47<br />
4.2 Zusammenfassung der Verfahren . . . . . . . . . . . . . . . . . . . 55<br />
4.3 Scankosten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57<br />
4.4 Sort- Hash- <strong>und</strong> Tree-Kosten . . . . . . . . . . . . . . . . . . . . . 58<br />
4.5 Join-Kosten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59<br />
4.6 Metadaten für die Relation R . . . . . . . . . . . . . . . . . . . . 61<br />
4.7 Metadaten für die Relationen R1 <strong>und</strong> R2 . . . . . . . . . . . . . . 63<br />
6.1 Metadaten für die Relation R1 . . . . . . . . . . . . . . . . . . . . 87<br />
6.2 Metadaten für die Relation R2 . . . . . . . . . . . . . . . . . . . . 87<br />
A.1 Rule based optimizer . . . . . . . . . . . . . . . . . . . . . . . . . 112<br />
A.2 Cost based optimizer mit hints . . . . . . . . . . . . . . . . . . . . 113<br />
127
128 TABELLENVERZEICHNIS
Literaturverzeichnis<br />
[1] J. Albrecht, Anfrageoptimierung in Data-Warehouse-Systemen auf Gr<strong>und</strong>lage<br />
des multidimensionalen Datenmodells, Dissertation, Gruner Druck<br />
GmbH, Januar 2001.<br />
[2] L. Becker, Ein Optimierer für ein erweiterbares Geo-Datenbanksystem, Diplomarbeit,<br />
Universität Dortm<strong>und</strong>, 1988.<br />
[3] L. Becker, R. H. Güting, Rule-Based Optimization and Query Processing in<br />
an Extensible Geometric Database System, ACM Transactions on Database<br />
Systems, Vol. 17 Nr. 2, pp. 247-303, 1992.<br />
[4] Siegfried Bell, The Expanded Implication Problem of Data Dependencies.<br />
LS8-Report, 16, University Dortm<strong>und</strong>, Computer Science VIII, 1995.<br />
[5] Siegfried Bell, Discovery and Maintenance of Functional Dependencies by<br />
Independencies, First International Conference on Knowledge Discovery in<br />
Databases, U.M. Fayyad (ed.), AAAI-Press, 1995.<br />
[6] Siegfried Bell, Deciding Distinctness of Query Results by Discovered Constraints.<br />
Proceedings of the Second International Conference on the Practical<br />
Application of Constraint Technology, Mark Wallace (ed.), The Practical<br />
Application Company Ltd., 1996.<br />
[7] Siegfried Bell, Entdeckung von Metadaten zur semantischen Anfrageoptimierung<br />
in relationalen <strong>Datenbanken</strong>. Berichte aus der Informatik, Shaker Verlag,<br />
1997.<br />
[8] S. Ceri, G. Pelagatti Distributed Databases, Principles ans Systems,<br />
McGraw-Hill Book Company, p. 133, 1984.<br />
[9] S. Chaudhuri, K. Shim, Optimization of queries with user-defined predicates,<br />
In Proceedings of the 22nd International Conference on Very Large Data<br />
Bases, pp. 87-98, Mumbai (Bombay), September 1996.<br />
[10] M. Cherniack, S. B. Zdonik, Rule languages and internal algebras for rulebased<br />
optimizers, In Proceedings of the 1996 ACM SIGMOD International<br />
Conference on Management of Data, pp. 401-412, Montreal, June 1996.<br />
129
130 LITERATURVERZEICHNIS<br />
[11] M. Cherniack, S. B. Zdonik, Changing the rules: Transformations for rulebased<br />
optimizers, In Proceedings of the 1998 ACM SIGMOD International<br />
Conference on Management of Data, pp. 61-72, Seattle, June 1998.<br />
[12] S. Cluet, G. Moerkotte, Query Optimization Techniques Exploiting Class<br />
Hirarchies, Aachener Informatik-Berichte, 1995.<br />
[13] R. L. Cole, G. Gräfe, Optimization of dynamic query evaluation plans, In<br />
Proceedings of the 1994 ACM SIGMOD International Conference on Management<br />
of Data, pp. 150-160, Minneapolis, June 1994.<br />
[14] E. F. Codd, A relational Model of Data for Large Shared Data Banks, In<br />
Communications of the ACM, p.13, 1970.<br />
[15] P. Dadam, Verteilte <strong>Datenbanken</strong> <strong>und</strong> Clien/Server-Systeme, Springer-<br />
Verlag Berlin Heidelberg, 1. Auflage, 1996.<br />
[16] S. Dieker, Efficient Integration of Query Algebra Modules into an Extensible<br />
Database Framework, Dissertation, Mensch & Buch Verlag, 2001.<br />
[17] Michael C. Ferrism, A Genetic Algorithm for Database Query Optimization,<br />
Artikel BFI97a, pp. 400-407, 1997.<br />
[18] J.C. Freytag, D. Maier, G. Vossen (Eds.), Query Processing for Advanced<br />
Database Applications, Morgan Kaufmann, 1994.<br />
[19] G. Gräfe, W. McKenna, The Volcano optimizer generator: Extensibility and<br />
efficient search, In Proceedings of the 9th International Conference on Data<br />
Engineering, pp. 209-218, Vienna, April 1993.<br />
[20] G. Gräfe, Query Evaluation Techniques for Large Databases, ACM Computing<br />
Surveys Volume 25. No.2, pp. 79-170, June 1993.<br />
[21] G. Gräfe, The Cascades framework for query optimization Bulletin of the<br />
Technical Commitee on Data Engineering, 18(3):19-29, September 1995.<br />
[22] G. Gräfe, P. OŃeil, G. Gräfe, Multi-Table Joins Bitmapped Join Indices,<br />
SIGMOD Record, Volume 24, No. 3, pp. 8-11, 1999.<br />
[23] T. Härder, Realisierung von operationalen Schnittstellen, Datenbankhandbuch,<br />
P.C. Lockemann, J.W. Schmidt (Hrsg.), Springer-Verlag, 1987.<br />
[24] J. M. Hellerstein, M. Stonebraker, Optimizing queries with expensive predicates.<br />
In Proceedings of the 1993 ACM SIGMOD International Conference<br />
on Management of Data, pp. 267-286, Washington, May 1993.<br />
[25] J. M. Hellerstein. Practical predicate placement, In Proceedings of the 1994<br />
ACM SIGMOD International Conference on Management of Data, pp. 325-<br />
335, Minneapolis, June 1994.
LITERATURVERZEICHNIS 131<br />
[26] Y. E. Ioannidis, V. Poosala, Balancing histogram optimality and practicality<br />
for query result size estimation. In Proceedings of the 1995 ACM SIGMOD<br />
International Conference on Management of Data, pp. 233-244, San Jose,<br />
May 1995.<br />
[27] M. Jarke, J. Koch, Query Optimization in Database Systems, ACM Computing<br />
Surveys Volume 16. No.2, pp. 111-152, June 1984..<br />
[28] N. Kabra, D. J. DeWitt, Efficient mid-query re-optimization of sub-optimal<br />
query execution plans. In Proceedings of the 1998 ACM SIGMOD International<br />
Conference on Management of Data, pp. 106-117, Seattle, June 1998.<br />
[29] A. Kemper, A. Eickler, Datenbanksysteme, 4. Auflage, Oldenbourg Wissenschaftsverlag<br />
GmbH, 2001.<br />
[30] W. Kim, D.S. Reiner, D.S. Batory (Eds.), Query Processing in Database<br />
Systems, Springer-Verlag, 1985.<br />
[31] W. Lehner, Erweiterte Konzepte <strong>und</strong> Techniken der Anfrageoptimierung in<br />
Datenbanksystemen, Arbeitsbericht, Gruner Druck GmbH, Februar 1998.<br />
[32] U. Lipeck, Datenbanksysteme I-III, Vorlesungscript, WS/SS 2001/2002<br />
[33] St. Manegold, J.K. Obermaier, F. Waas, Flexible Anfrageoptimierung in<br />
parallelen Datenbanksystemen, Arbeitspapier, Humboldt-Universität zu Berlin,<br />
Januar 1997.<br />
[34] V. Markl, Mistral: Processing Relational Queries using a Multidimensional<br />
Access Technique, Dissertation, Handt Druck GmbH, 1999.<br />
[35] H. Meuss, Logical Tree Matching with Complete Answer Aggregates for Retrieving<br />
Structured Documents, Dissertation, Dissertation.de-Verlag, 2000.<br />
[36] B. Mitschang, Anfragebearbeitung in Datenbanksystemen, 1. Auflage, Vieweg<br />
Verlag, 1995.<br />
[37] H. Garcia-Molina, J. D. Ullman, J. Widom, Database System Implementation,<br />
1. Auflage, Prentice-Hall Inc., 2000.<br />
[38] G. Moerkotte, Konstruktion von Anfrageoptimierern für Objektdatenbanken,<br />
Habilitation, Shaker Verlag, 1995.<br />
[39] Oracle 9i, Server Concepts Manual, Kapitel 13: The Optimizer.<br />
[40] M. Tamer Özsu, Patrick Valduriez, Principles of Distrubuted Database Systems,<br />
2. Auflage, Prentice-Hall Inc., 1999.<br />
[41] G. Piatesky-Shapiro, C. Connell, Accurate Estimation of the Number of Tuples<br />
Satisfying a Condition, In Proceedings of the ACM SIGMOD Conference,<br />
Boston, 1984.
132 LITERATURVERZEICHNIS<br />
[42] V. Poosala, Y. E. Ioannidis, P. J. Haas, and E. J. Shekita. Improved histograms<br />
for selectivity estimation of range predicates. In Proceedings of the<br />
1996 ACM SIGMOD International Conference on Management of Data, pp.<br />
294-305, Montreal, June 1996.<br />
[43] V. Poosala, Y. E. Ioannidis, Selectivity estimation without the attribute value<br />
independence assumption. In Proceedings of the 23rd International Conference<br />
on Very Large Data Bases, pp. 486-495, Athens, August 1997.<br />
[44] H. Riedel, Effiziente Anfrageauswertung in objektorientierten <strong>Datenbanken</strong>,<br />
Dissertation, Shaker Verlag, 1994.<br />
[45] S. Russel, P. Norvig, Artificial Intelligence A Modern Approach, Prentice<br />
Hall Verlag, 1995.<br />
[46] G. Saake, A. Heuer, <strong>Datenbanken</strong>: Implementierungstechniken, 1. Auflage,<br />
mitp-Verlag GmbH, 1999.<br />
[47] Y. C. Sagiv, Optimization of Queries in Relational databases, 1. Auflage,<br />
UMI Research Press, 1981.<br />
[48] W. Scheufele, G. Moerkotte, Efficient dynamic programming algorithms for<br />
ordering expensive joins and selections. In Proceedings of the 6th International<br />
Conference on Extending Database Technology, pp. 201-215, Valencia,<br />
March 1998.<br />
[49] W. Scheufele, G. Moerkotte, Optimal Ordering of Selections and Joins in<br />
Acyclic Queries with Expensive Predicates, Aachener Informatik-Berichte,<br />
1996.<br />
[50] H. Schöning, Anfrageverarbeitung in Komplexobjekt- Datenbanksystemen,<br />
Dissertation, Deutscher Universitätsverlag GmbH, 1993.<br />
[51] P. Selinger, M. Astraham, D. Chamberlain, R. Lorie, T. Price, Access Path<br />
Selection in a Relational Database Management System, In Proceedings of<br />
the ACM SIGMOD Conference, 1979.<br />
[52] M. Steinbrunn, G. Moerkotte, A. Kemper, Optimizing Join Orders, Universität<br />
Passau, Fakultät für Mathematik <strong>und</strong> Informatik Universität Passau,<br />
1993.<br />
[53] M. Steinbrunn, G. Moerkotte, A. Kemper, Heuristic and randomized optimization<br />
for the join ordering problem, The VLDB Journal, 6(3):191-208,<br />
1997.<br />
[54] M. Utesch, Ein Beitrag zur Anfrageoptimierung in DBS mit Genetischen<br />
Algorithmen, 1. Auflage, VDE-Verlag, 2000.
LITERATURVERZEICHNIS 133<br />
[55] P. Valduriez, Join Indices, ACM TODS, Volume 12, pp. 218-246, Nr. 2, Juni<br />
1991.<br />
[56] B. Vance, D. Maier, Rapid bushy join-order optimization with Cartesian products.<br />
In Proceedings of the 1996 ACM SIGMOD International Conference<br />
on Management of Data, pp. 35-46, Montreal, June 1996.<br />
[57] G. Vossen, Datenmodelle, Datenbanksprachen <strong>und</strong> Datenbankmanagementsysteme,<br />
Kapitel 15., pp. 471-497, 4. Auflage, Oldenbourg Wissenschaftsverlag<br />
GmbH, 2000.
Nachwort<br />
Diese Diplomarbeit entspricht mehr als sechs Monaten meines Lebens, in denen<br />
ich mir fast jeden Tag ausnahmslos Gedanken <strong>und</strong> Ideen bezüglich dieses Themas<br />
gemacht habe. Zum erstenmal wurde ich mir meiner Grenzen bewußt <strong>und</strong> legte<br />
mir selbst auf, sie so oft zu durchbrechen, wie es mir möglich war.<br />
Als es damals darum ging, ob ich jemals ein Gymnasium besuchen dürfte, waren<br />
sich - außer meinen Eltern - alle im Klaren, daß ich nie für eine höhere Schullaufbahn<br />
geeignet sein würde. Jetzt - nach 13 Jahren - soll diese Arbeit das Gegenteil<br />
beweisen.<br />
Perfektionismus, Masochismus <strong>und</strong> ein bisschen dem Verlangen es all denen recht<br />
zu machen, die große Erwartungen in mich gesetzt haben, hoffe ich Genüge getan<br />
zu haben.<br />
Vieles was man erreichen möchte, kann man nur dann erlangen, wenn man dafür<br />
etwas Anderes aufgibt. Ich bete zu Gott, daß ich nicht zu viele Dinge geopfert<br />
habe, die ich nicht mehr gutmachen kann.<br />
Mazeyar Eghballossaltaneh Makoui<br />
”Zeit ist irrelevant; Raum ist irrelevant;<br />
das Einzige was zählt, ist das jetzt <strong>und</strong> heute.”