09.02.2013 Aufrufe

pdf (870 Kb) - Fachgebiet Datenbanken und Informationssysteme

pdf (870 Kb) - Fachgebiet Datenbanken und Informationssysteme

pdf (870 Kb) - Fachgebiet Datenbanken und Informationssysteme

MEHR ANZEIGEN
WENIGER ANZEIGEN

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.”

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!