UNIVERSITÄT KONSTANZ Database Tuning & Administration Dokumentation: Oracle 10g Express & CentOS 4.4 Michael Seiferle (596485) Marco Matt (591693) Sommersemester 2007 Fachbereich Informatik & Informationswissenschaft Database & Information Systems Group Prof. Marc H. Scholl Christian Grün I NHALTSVERZEICHNIS 1 Aufgabenstellung ............................................................................................................................................................................... 4 2 Installation & Konfiguration ............................................................................................................................................................... 4 2.1 CentOS 5.0 .................................................................................................................................................................................. 4 2.2 CentOS 4.4 .................................................................................................................................................................................. 4 2.3 Oracle 10g Express Edition.......................................................................................................................................................... 4 3 Benchmark ......................................................................................................................................................................................... 6 3.1 Erste Schritte: CDDB Daten ......................................................................................................................................................... 6 3.1.1 Vorbereitung DDL ................................................................................................................................................................ 6 3.1.2 Einlesen der Daten (sqlplus & sqlldr) ................................................................................................................................... 6 3.1.3 Queries ................................................................................................................................................................................. 7 3.2 TPC-H Benchmark ....................................................................................................................................................................... 7 3.2.1 Datenbasis generieren und einlesen ................................................................................................................................... 8 3.2.2 Queries generieren .............................................................................................................................................................. 8 3.2.3 Erste Tuningmaßnahmen ..................................................................................................................................................... 9 3.2.4 Wie wir gemessen haben ..................................................................................................................................................... 9 4 Tuning einzelner Queries ................................................................................................................................................................. 11 4.1 Query 14 ............................................................................................................................................................................... 11 4.2 Query 15 ............................................................................................................................................................................... 11 4.3 Query 9 ................................................................................................................................................................................. 11 4.4 Query 2 ................................................................................................................................................................................. 12 4.5 Query 3 ................................................................................................................................................................................. 12 4.6 Query 6 ................................................................................................................................................................................. 12 4.7 Query 18 ............................................................................................................................................................................... 12 4.8 Verändern der Buffer Größe ................................................................................................................................................. 13 5 Fazit .................................................................................................................................................................................................. 14 Anhang ................................................................................................................................................................................................ 15 2 convert.py ....................................................................................................................................................................................... 15 CDDB Queries ................................................................................................................................................................................. 16 Beispiel Control-File für SQL-Loader (Tabelle Customer TPCH) ...................................................................................................... 16 Primär- und Fremdschlüssel des TPC-H Schemas ........................................................................................................................... 16 TPC-H Querys .................................................................................................................................................................................. 17 Query 2 ....................................................................................................................................................................................... 17 Query 3 ....................................................................................................................................................................................... 18 Query 6 ....................................................................................................................................................................................... 18 Query 9 ....................................................................................................................................................................................... 18 Query 14 ..................................................................................................................................................................................... 19 Query 15 ..................................................................................................................................................................................... 19 Query 18 ..................................................................................................................................................................................... 20 Framwork in Java ............................................................................................................................................................................ 20 Class BenchmarkTest.java ........................................................................................................................................................... 20 Class Query.java .......................................................................................................................................................................... 22 Class QueryLoader.java ............................................................................................................................................................... 23 Bibliographie ....................................................................................................................................................................................... 24 3 1 A UFGABENSTELLUNG Ziel der Lehrveranstaltung war es ein DBMS samt zu Grunde liegendem Betriebssystem aufzusetzen, zu konfigurieren und zu tunen. Das Augenmerk wurde hier vor allem auf das Tuning der Queries des TPCH Benchmarks gelegt. Der TPCH Benchmark beinhaltet in erster Linie Anfragen die in Decision Support Systemen vorkommen und auf großen Datenmengen ohne Vorwissen operieren [1]. Gleich zu Beginn mussten wir uns zwischen den Betriebssystemen Microsoft Windows XP oder einer Linux Distribution unserer Wahl entscheiden. Außerdem standen die Datenbankmanagementsysteme Microsoft SQL Server 2005 Express, IBMs DB2 Express Edition oder Oracle 10g Express Edition (10.2) zur Auswahl. Unsere Wahl fiel auf CentOS und Oracle Express. Da CentOS binärkompatibel zu Red Hat Enterprise Linux (im folgenden RHEL) ist und Oracle seine DBMS für RHEL zertifiziert, erschien uns diese Auswahl sinnvoll. 2 I NSTALLATION & K ONFIGURATION 2.1 C ENT OS 5.0 Die Installation von CentOS 5.0 (CD) war auf Grund von inkompatiblen Gerätetreibern nicht möglich. Die Installations-CD erkannte den verwendeten SATA-Controller nicht und weigerte sich nach der Installation des Basissystems das CD-Laufwerk anzusprechen und die Installation fortzusetzen. Nach Studium der Hardware Dokumentation versuchten wir unser Glück mit einer Installation über den FTP-Server des CentOS Projekts. Die Installation verlief soweit erfolgreich allerdings hatten wir nach dem ersten Reboot Probleme mit dem Device Manager udev, die sich auch mit mehreren Lösungsansätzen nicht beheben ließen. 2.2 C ENT OS 4.4 Die Installation von CentOS 4.4 verlief hingegen ohne nennenswerte Probleme. Die komplette Hardware inklusive beider Prozessorkerne wurde erkannt. Allerdings installierte die Update Automatik nach der Installation unbemerkt das Update auf CentOS 4.5 welches im Kernel erneut keine Unterstützung für den SATA-Controller bereithielt. Glücklicherweise blieb der alte Kernel weiterhin auf dem System installiert, so dass wir ihn nur im Bootmanager als Standardkernel eintragen mussten. Danach installierten wir noch SSH um per Remote Login auf dem Rechner arbeiten zu können. 2.3 O RACLE 10 G E X PRESS E DITION Für die Installation des Oracle DBMS meldeten wir uns im Oracle Technology Network [2] an und konnten die aktuelle Version als RPM-Paket herunterladen. Danach starteten wir die Installation auf der Konsole: $ rpm -ivh downloads/oracle-xe-univ-10.2.0.1-1.0.i386.rpm und konfigurierten die frisch installierte Datenbank mit folgendem Befehl: 4 $ /etc/init.d/oracle-xe configure Wir beließen alle Einstellungen auf ihrem voreingestellten Wert und vergaben lediglich das Passwort für den SYSTEM Account. Nach einem Reboot des kompletten Systems legten wir zunächst eingeschränkte Benutzerkonten für die weitere Arbeit mit Hilfe des Webinterfaces der Datenbank an. Um auch über den Konsolenclient sqlplus auf die Datenbank zugreifen zu können waren zusätzliche Maßnahmen notwendig; sqlplus benötigt einige Umgebungsvariablen um zu starten: # vi ~/.bash_profile ORACLE_BASE=/usr/lib/oracle/xe/app/oracle/product/10.2.0export ORACLE_BASEORACLE_HOME=$ORACLE_BASE/serverexport ORACLE_HOMEORACLE_SID=XEexport ORACLE_SIDPATH=.:$PATH:$ORACLE_HOME/bin:$ORACLE_HOME/libLD_LIBRARY_PATH=$LD_LI BRARY_PATH:$ORACLE_HOME/libORA_NLS33=$ORACLE_HOME/ocommon/nls/admin/dataCLASSP ATH=$ORACLE_HOME/JRE:$ORACLE_HOME/jlibexport LIBPATH=$LIBPATH:$ORACLE_HOME/libexport ORACLE_BASE ORACLE_HOME PATH LD_LIBRARY_PATH ORA_NLS33 CLASSPATH 5 3 B ENCHMARK 3.1 E RSTE S CHRITT E : CDDB D ATEN Unsere nächste Aufgabe war es das Datenbanksystem samt der mitgelieferten Werkzeuge kennenzulernen. Hierzu sollten wir zunächst einen Auszug der CDDB Daten[3] als PostgreSQL-Dump in die Datenbank einlesen, dazu fünf Queries definieren und mit ersten Tuningmaßnahmen beginnen. 3.1.1 V O R BE R EI T UN G DDL Da die Syntax der Schemadefinitionen von Oracle und PosgreSQL weitgehend kompatibel sind (vergleiche [4] & [5]) konnten wir die entsprechenden CREATE Statements einfach aus dem Schema Dump kopieren und mussten nur wenige Schlüsselwörter durch ihre Pendants in Oracle ersetzen. Namentlich waren das VARCHAR durch VARCHAR2. 3.1.2 E I N L E S EN DE R D AT EN ( SQ LP L US & SQ L L DR ) Da der PostgreSQL Dump die Daten nicht als INSERT-Statements enthält, sondern als tabulatorgetrennte Spalten, schrieben wir zunächst ein kleines Python-Skript [Anhang], welches den Reintext in INSERTStatements umwandelt und in einer Textdatei als SQL-Skript speichert. Danach galt es noch die Sonderzeichen wie & und ' zu escapen: sqlplus erwartet die Ersetzung jedes & durch eine Benutzereingabe. Mit Hilfe des Konsolenclients sqlplus lassen sich so generierte SQL-Anweisungen dann einfach einlesen: $ sqlplus Benuter/Passwort $ @<Pfad zu>dss.ddl Da das Einlesen der Daten jedoch schon für das vergleichbar kleine Datenvolumen unerwartet langsam war, bemühten wir uns um ein Bulk Loading Utility [6], welches wir im mitgelieferten sqlldr fanden. Das Kommandozeilentool sqlldr benötigt, um die Daten zu importieren eine Steuerdatei. In dieser ist spezifiziert aus welcher Datendatei und in welchem Format die Daten in eine Tabelle der Datenbank importiert werden müssen. Das Control File für cddb.genres hat beispielsweise folgendes Format: # sqlldr Control File for cddb.genres load data infile '/home/michael/csv/csv/genres.csv' into table genres fields terminated by "\t" optionally enclosed by '"' (genreid,genre) Der Import selbst wird durch den Befehl 6 $ sqlldr benutzer/passwort@datenbank control=cddb.genres.ctl gestartet. 3.1.3 Q U ERI E S Die nächste Aufgabe bestand darin, Queries für die neu erstellte Datenbasis zu entwerfen und erste Tuningmaßnahmen zu ergreifen um diese Anfragen zu beschleunigen. Da es in erster Linie darum ging möglichst aufwändige Queries zu schreiben, haben wir keinen großen Wert auf den Sinn oder die Alltagsrelevanz der Anfragen gelegt. Mit den von uns formulierten Queries [Anhang] haben wir uns bemüht, möglichst alle Anfrageszenarien abzudecken: Punktanfragen, Rangeanfragen, Aggregationen, Joins und Pattern Matching. Im Zuge der Optimierungen erstellten wir Primär- und Fremdschlüssel und belegten geeignete Felder mit Constraints. Zusätzlich erstellten wir Indizes für die Volltextsuche von der der LIKE Operator profitierte. 3.2 TPC-H B ENCHMARK Der TPC Benchmark H ist ein Decision-Support-Benchmarksystem. Es besteht aus 22 businessorientierten Abfragen bzw. parallelablaufenden Datenmanipulationen. In unserem Test benutzten wir Version 2.6.0. Datenbasis des Benchmarks bildeten 8 Tabellen von zum Teil erheblich unterschiedlicher Größe. Die Beziehungen der einzelnen Tabellen werden in Abbildung 1 dargestellt. A BBILDUNG 1: TPC-H S CHEMA 7 Wie aus der Abbildung ersichtlich, bildet der TPC-H Benchmark die Geschäftsprozesse eines Handelsunternehmens ab. Es bezieht Waren (Parts) von Lieferanten (Supplierer). Es wird festgehalten, welche Waren, mit Preis und Anzahl, von welchem Lieferanten bezogen werden können (Partsupp). Kunden (Customer) können Bestellungen (Orders) aufgeben, die aus mehreren Bestellposten (Lineitem) bestehen können. Kunden und Lieferanten können unterschiedlicher Nationalität (Nation) und somit auch aus unterschiedlichen Kontinenten (Region). 3.2.1 D AT EN B ASI S GE N E R I E R EN U N D EI N L E S EN Die Tabellen und die Datenbasis werden mittels Skript automatisch generiert. Dazu musste im Makefile der Compiler (in unserem Fall gcc), das Betriebssysten (LINUX) und der Benchmarktyp (TPCH) bestimmt werden. Da Oracle als Datenbank nicht wählbar war, benutzten wir DB2. Nach dem Kompilieren musste das Skript dbgen ausgefüht werden, das die DDL der Tabellen und die Datenbasis erstellt. Hierbei ist eine unterschiedliche Skalierung der Größe der zufällig erstellten Datenbasis möglich. Wir wählten eine Größe von 1 GB. $ run dbgen -v -s 1 Das Erstellen der Tabellen der Datenbank geschah mit Hilfe des Kommandozeilentools sqlplus und der erstellten DDL. $ sqlplus Benuter/Passwort $ @<Pfad zu>dss.ddl Die Daten wurden, wie schon bei der cddb-Datenbank mit Helfe des Tools sqlloader in die Tabellen eingefügt. $ sqlldr benutzer/passwort@datenbank control=<Name>.ctl Bis auf die Angabe des richtigen Datumformats (date yyyy-mm-dd) musste hier nichts weiter beachtet werden [Anhang]. 3.2.2 Q U ERI E S G EN E R I ER EN Die vorgegebenen Anfragen sollten durch das Programm qgen, mit Zufallswerten gefüllt, erstellt werden. $ run qgen -N -d 1 > 1_g.sql 8 Da die Generierung der Anfragen bei uns mit Fehlermeldung abbrach und im Internet gefundene Lösungsansätze keinen Erfolg brachten, ersetzten wir die Variablen durch selbst gewählte und an die Datenbasis angepasste Werte. 3.2.3 E R ST E T UN I N G M Aß N A HM EN Da alle Anfragen mit selbst ausgesuchten Werten ergänzt wurden, haben wir als erstes diese Anfragen ohne Primär- und Fremdschlüsselbedingungen getestet. Dabei gab es schon Anfragen, die nach einigen Minuten abgebrochen werden mussten. Durch das Anlegen der Primär- und Fremdschlüsselbedingungen [Anhang] und den damit von Oracle erstellten Indexe auf die Primärschlüssel in Form von B-Trees konnten keine generellen Verbesserungen der Abfragezeit gemessen werden. Einige Abfragezeiten verbesserten sich erheblich, andere Abfragen wurden daduch sogar langsamer. Ein möglicher Grund für die Verlangsamung der Anfrage bei Benutzung von Indizes könnte sein, da unser Rechner über zu wenig RAM verfügte, so dass Teile der gecachten Tabellen aus dem Buffer entfernt werden mussten, um den Index zu cachen. Die Tabellen müssen später wieder neu eingelesen werden. Die vom Advisor der Web-Oberfläche vorgeschlagenen Indizes brachten ebenfalls keine merklichen Verbesserungen der Abfragezeiten, weshalb wir generell keine benutzt haben und weitere Indizes nur zur Optimierung der einzelnen Anfragen nutzten. Das Anlegen weiterer Index-Strukturen wie Hash clustered oder B-clusterd Index ist in der Express-Ausgabe von Oracle nicht möglich. 3.2.4 W I E W I R G E M ES S EN HA B E N Um verlässliche Meßergebnisse zu erhalten, wurde jede Query mindestens sechs Mal ausgeführt. Dabei wurde die erste Zeit ignoriert und danach der Durchschnitt der verbleibenden fünf Zeiten gebildet. Da wir nicht sicher sagen konnten, ob die Veränderung des Schemas (beispielsweise Indizes deaktivieren) nicht vielleicht doch Auswirkungen auf nachfolgende Queries hatte, erstellten wir zwei identische Datenbanken des TPC-H Benchmarks und ließen davon eine gänzliche unoptimiert, um verlässliche Vergleichswerte zu erhalten. Um die Messergebnisse von äußeren Einflüssen weitgehend unabhängig zu machen, hatten wir uns überlegt in Java ein Benchmark Framework zu erstellen, das über den Oracle Net Listener von einem Remoterechner die Anfragen sendet. Außerdem sollten die Möglichkeiten von Oracle ein Tracefile zu erstellen, in dem detaillierte Angaben zu CPU-Cycles, der Speicherauslastung und den Query Plänen gespeichert sind genutzt werden. Dieses Tracefile lässt sich mit dem Tool tkprof parsen und generiert daraus einen ausführlichen Report. Der Java Rumpf unseres Frameworks ist als [Anhang] der Arbeit beigefügt. Leider scheiterten wir mit unserem Framework aber an zwei Problemen: 9 Die von Oracle generierten Tracefiles enthielten zwar alle relevanten Informationen, jedoch ließ sich der Name unter dem die Tracefiles abgelegt wurden nicht verändern oder abfragen. Außerdem wurde mit dem Parsen des Tracefiles der Query Plan neu von der Datenbank angefragt, so dass wir nach jeder Tracefile Generierung sofort den Report manuell generieren mussten. Wir gingen deshalb dazu über für unsere Benchmarks nur noch die Ausführungszeiten und die jeweiligen Query Pläne in Betracht zu ziehen, die wir mit dem Tool Oracle SQL Developer [7] ausführten und erstellten. 10 4 T UNING EINZELNER Q UERIES 4.1 Q U ER Y 14 Query 14 war für uns vor allem interessant, weil es gleiche mehrere teure Operationen verknüpft: Intervallsuche, Bedingte Selektion mit String Matching und einen Join. Schon durch das hinzufügen eines Primärschlüssels auf eines der beiden Join-Attribute partsupplier.ps_partkey konnte die Ausführungszeit von 15,7 auf ~0,51 Sekunden gesenkt werden. Den zusätzlich definierten Fremdschlüssel auf l_partkey wird dabei vom Scheduler nicht berücksichtigt und ließ sich auch durch einen HINT nicht erzwingen. 4.2 Q U ER Y 15 Bei diesem Query, welches neben einer View Creation auch 2 Aggregationen enthält, galt es vor allem die Anzahl der Physical Reads, also das Lesen der Blöcke von der Festplatte, zu reduzieren. Ohne Optimierungen wurden zur Durchführung der Anfrage über 100 000 Blöcke gelesen, was die Ausführung des Queries, mit knapp über 17 Sekunden, stark verlangsamte. Die Analyse der Query Plans zeigte, dass vor allem der FULL TABLE ACCESS auf lineitems, in unserem Fall eine Relation mit über 5 Millionen Datensätzen, für die große Anzahl an Physical Reads verantwortlich war. Da auf lineitems eine RANGE selektiert wird und auf dem Attribut l_shipdate kein Index definiert ist, werden hier erst alle Zeilen gelesen und danach diejenigen aussortiert die dem Selektionskriterium nicht entsprechen. Das spiegelt sich auch im Query Plan wieder, der als Zugriffspfad "Filter Predicates" für l_shipdate angibt. Den Auswirkungen dieses Zugriffspfades war durch einen Index auf l_shipdate gut beizukommen: Der Scheduler wählt "Access Predicates" als Zugriffspfad auf die Daten: Wir vermeiden somit den Full Table Scan und greifen stattdessen über den Index auf die Tabelle zu. Das senkt die Laufzeit in unserem Fall von über 17 auf ~1,02 Sekunden. 4.3 Q U ER Y 9 Mit Query 9 förderten wir ein interessantes Phänomen zu Tage: Oracle scheint sich den günstigsten Ausführungsplan zu merken - und lässt sich auch durch Compiler Hints nicht mehr davon abbringen. Query 9 berechnet den Gewinn pro Jahr und Land für alle Produkte, deren Name dem Muster '%snow' entspricht. Hierzu wird eine Subquery benutzt die praktisch alle Tabellen der Datenbank (PART, SUPPLIER, LINEITEM, PARTSUPP, ORDERS, NATION) via NATURAL JOIN verknüpft und daraus alle PARTS entfernt die dem Pattern '%snow' nicht entsprechen. Ohne Optimierungen benötigte die Query ~28 Minuten, waren Indizes, Schlüssel und Constraints angelegt benötigte die Anfrage immerhin noch etwas über 3 Minuten. Um die Geschwindigkeit weiter zu verbessern versuchten wir dem Compiler mit HINTS zu helfen; sinnvoll erschien uns hier vor allem die Angabe des /* ORDERED */ Flags, es teilt dem Compiler mit alle Joins in der angegeben Reihenfolge durchzuführen. Das brachte schon ohne Umstellung der Reihenfolge (am wichtigsten war es hier so früh wie möglich nur die Parts zu selektieren, die der WHERE Bedingung auch entsprechen) einen 11 enormen Zuwachs in der Ausführungsgeschwindigkeit: Die Anfrage benötigte nur mehr 95 Sekunden. Überrascht hingegen waren wir, als wir der HINT /* PUSH_SUBQ */ ausprobierten, der das innere Query so früh wie möglich zur Ausführung bringen sollte. Die Ausführungszeit sank um weitere 60% auf nur noch 29s. Erklären konnten wir uns dieses Verhalten zunächst nicht, zumal die Anfrage auch so aufgebaut ist, dass das SUBQUERY die einzige Quelle für Tupel im Äußeren Query darstellt; also ohnehin so früh wie möglich zur Ausführung gebracht werden muss. Auch der Queryplan wies zwischen den beiden HINTs keine Unterschiede auf, was zu der Vermutung führte, dass unser HINT auf Grund der Erstellung der im Ausführungsplan aufgeführten temporären View irrelevant wurde[7]. 4.4 Q U E RY 2 Query 2 ist eine Anfrage mit einer Aggregation in der Subquery und Joins in Haupt- und Subquery. Weiterhin enthält sie im Haupteil ein Matching auf einen Substring. Das Resultat soll nach drei Attributen (Supplier.acctbal, Nation.name, Supplier.name, Part.partkey) geordnet zurückgegeben werden. Ohne Indizes brauchte diese Anfrage durschnittlich 2,84 Sekunden. Durch Indizes konnte diese Zeit auf durchschnittlich 0,431 Sekunden verbessert werden, da hier Sortierung im Buffer und Bildung des Kartesischen Produkts vermieden werden konnte. Weitere Indizes oder die Angabe von Hints verbesserten die Geschwindigkeit der Anfrage nicht bzw. führten zu keiner Änderung des Ausführungplans. 4.5 Q U E RY 3 Query 3 berechnet den Erlös durch Bestellungen die vor einem bestimmten Datum aufgegeben, aber noch nicht vollständig ausgeliefert worden sind in absteigender Reihenfolge. Sie beinhaltet somit eine Aggregation, Gruppierung und Sortierung. Ohne Optimierung brauchte sie über zwei Minuten zur Ausführung. Mit Indizes konnte die Ausführungszeit auf unter 20 Sekunden reduziert werden. Weitere Indizes auf orderdate und shipdate oder die Angabe von Hints verbesserten die Geschwindigkeit der Anfrage nicht bzw. führten zu keiner Änderung des Ausführungplans. 4.6 Q U E RY 6 Query 6 berechnet die Erhöhung der Erlöse in einem bestimmten Zeitraum, wenn ein gewährter Nachlass bei weniger als 24 Teilen nicht gegeben worden wäre. Mit und ohne Indizes brauchte diese Anfrage 15 Sekunden. Ein Index auf discount brachte auch keine weitere Verbesserung. 4.7 Q U ER Y 18 Query 18 beinhaltet eine Subquery auf der großen Tabelle lineitems. In der Hauptquery werden die Tabellen customer, orders und lineitems gejoint. Es findet ein Gruppierung und eine Sortierung des Resultats statt. Mit und ohne Indizes brauchte diese Anfrage jeweils über 80 Sekunden. Erklärung hierfür ist, dass für die 12 Gruppierungen, welche die teuersten Operationen in dieser Anfrage sind, keine Indizes verwendet werden und somit kein Unterschied messbar ist. Durch das Ablegen der Subquery in einer Materialized View wurde die Abfrage in durchschnittlich 43 Sekunden ausgeführt. Weitere Optimierungsversuche wie die Änderung der Joinreihenfolge und die Änderung der Joindurchführung durch Hints brachten keinen messbaren Erfolg. 4.8 V ER ÄN D ER N D ER B UF F E R G R Ö ß E Oracle unterscheidet im Wesentlichen drei Kategorien von Buffer. Im Keep-Pool werden Informationen, die lange gehalten werden sollen, gelagert, im Recycle-Pool Informationen, die schnell wieder gelöscht werden können um im Default-Pool Informationen, die den anderen beiden nicht zuzuordnen sind. Tabellen können mittels ALTER TABLE table_name STORAGE (buffer_pool KEEP); einem speziellen Pool zugeordnet werden. Eine Vergrößerung des Buffers bzw. die Zuordnung einzelner Tabellen zu einem bestimmten Pool brachten keine signifikanten Geschwindigkeitszuwächse der Anfragen. Eine Verkleinerung des Buffer-Pools hingegen brachte den erwarteten Geschwindigkeitsverlust der Anfragen. 13 5 F AZIT Im Laufe der Veranstaltung wurde uns schnell klar, dass ein großes DBMS wie Oracle im Bezug auf Performance, Leistungsumfang und Einarbeitungszeit nicht ohne weiteres zu vergleichen ist mit freien Datenbankmanagementsystemen wie MySQL oder PostgreSQL. Eine zusätzliche Hilfe waren Werkzeuge wie der Oracle SQLDeveloper, der das erstellen und parsen der Tracefiles zu jedem Query übernimmt. Wir bemerkten aber auch sehr schnell, dass uns die abgespeckte Version von Oracle 10.2 viele Funktionien vorenthielt die wir durchaus gerne ausprobiert hätten: Seien es die verschiedenen Indextypen, geclusterte Tabellen, oder auch die Möglichkeit vor allem die großen Tabellen komplett im Arbeitsspeicher des Rechner vorzuhalten. Die Arbeit mit Oracle war sehr interessant und brachte uns viele der theoretischen Konzepte, die wir aus vorangegangen Veranstaltungen kannten, auch praktisch näher. Auch wenn wir mit unseren Maßnahmen zur Verbesserung der Performance einige gute Erfolge erzielen (siehe Abbildung 2) konnten bleibt natürlich immer noch Raum für Verbesserungen. Für den Produktiveinsätz wäre zu überlegen, nicht die Express Edition von Oracle einzusetzen um in den Genuss der zusätzlichen Features zu kommen und die Performance weiter zu verbessern. A BBILDUNG 2: T UNING Ü BERSICHT 14 A NHANG CONVERT . PY #!/usr/bin/python # convert.py # convert postgres dump to std. sql import re infile = open("cddb.sql","r") outfile = open("outfile.sql","w+") copy_matcher = re.compile('COPY (\w*) \((.*)\)') stdin_match = re.compile('[0-9]+\w') komma_splitter = re.compile(',') current_table = "" cols = "" for line in infile: # check for COPY FROM STDIN STRING match = copy_matcher.match(line) match_data = stdin_match.match(line) insertstring = """" if match: # set new column & table names current_table = match.group(1) columns = re.split(komma_splitter,match.group(2)) cols = match.group(2) print "We found a matching line: ",current_table, columns # start reading columns for current_table if match_data: # yeehaw a line of data.... line = line.strip() line = line.replace('\'',"\'") words = line.split("\t") values = [] for word in words: word = "'",word.strip(),"'" values.append("".join(word)) insertstring = "INSERT INTO ",current_table," (",cols, ") VALUES (", ", ".join(values),");","\n" outfile.write(insertstring ) infile.close() outfile.close() 15 CDDB Q UERIES SELECT DISTINCT a.artist,al.album,s.song, genre from genres g,artists a,cds c, cdtracks ct, songs s, artist2album a2a, albums al WHERE (a.artist LIKE 'a%') AND a.artistid = a2a.artistid AND a2a.albumid = al.albumid AND a2a.artist2albumid = c.artist2albumid AND ct.cdid = c.cdid AND g.genreid = c.genreid AND ct.songid = s.songid B EISPIEL C ONTROL -F ILE FÜR SQL-L OADER (T ABELLE C USTOMER TPCH) ------------------------------------------------------------------------------- file: tpch-customer.ctl -----------------------------------------------------------------------------LOAD DATA INFILE ’tpch-customer.data’ INTO TABLE tpch.customer FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’"’ ( c_custkey, c_name, c_address, c_nationkey, c_phone, c_acctbal, c_mktsegment, c_comment) P RIMÄR - UND F REMDSCHLÜSSEL DES TPC-H S CHEMAS -- Primary key constraints ALTER ALTER ALTER ALTER ALTER TABLE TABLE TABLE TABLE TABLE tpchmm.part ADD CONSTRAINT c_pk01 PRIMARY KEY (p_partkey); tpchmm.supplier ADD CONSTRAINT c_pk02 PRIMARY KEY (s_suppkey); tpchmm.partsupp ADD CONSTRAINT c_pk03 PRIMARY KEY (ps_partkey, ps_suppkey); tpchmm.customer ADD CONSTRAINT c_pk04 PRIMARY KEY (c_custkey); tpchmm.orders ADD CONSTRAINT c_pk05 PRIMARY KEY (o_orderkey); 16 ALTER TABLE tpchmm.lineitem ADD CONSTRAINT c_pk06 PRIMARY KEY (l_orderkey, l_linenumber); ALTER TABLE tpchmm.nation ADD CONSTRAINT c_pk07 PRIMARY KEY (n_nationkey); ALTER TABLE tpchmm.region ADD CONSTRAINT c_pk08 PRIMARY KEY (r_regionkey); -- Foreign key constraints. ALTER TABLE tpchmm.supplier ADD CONSTRAINT c_fk01 FOREIGN KEY (s_nationkey) REFERENCES tpchmm.nation (n_nationkey); ALTER TABLE tpchmm.partsupp ADD CONSTRAINT c_fk02 FOREIGN KEY (ps_partkey) REFERENCES tpchmm.part (p_partkey); ALTER TABLE tpchmm.partsupp ADD CONSTRAINT c_fk03 FOREIGN KEY (ps_suppkey) REFERENCES tpchmm.supplier (s_suppkey); ALTER TABLE tpchmm.customer ADD CONSTRAINT c_fk04 FOREIGN KEY (c_nationkey) REFERENCES tpchmm.nation (n_nationkey); ALTER TABLE tpchmm.orders ADD CONSTRAINT c_fk05 FOREIGN KEY (o_custkey) REFERENCES tpchmm.customer (c_custkey); ALTER TABLE tpchmm.lineitem ADD CONSTRAINT c_fk06 FOREIGN KEY (l_orderkey) REFERENCES tpchmm.orders (o_orderkey); ALTER TABLE tpchmm.lineitem ADD CONSTRAINT c_fk07 FOREIGN KEY (l_partkey) REFERENCES tpchmm.part (p_partkey); ALTER TABLE tpchmm.lineitem ADD CONSTRAINT c_fk08 FOREIGN KEY (l_suppkey) REFERENCES tpchmm.supplier (s_suppkey); ALTER TABLE tpchmm.lineitem ADD CONSTRAINT c_fk09 FOREIGN KEY (l_partkey, l_suppkey) REFERENCES tpchmm.partsupp (ps_partkey, ps_suppkey); ALTER TABLE tpchmm.nation ADD CONSTRAINT c_fk10 FOREIGN KEY (n_regionkey) REFERENCES tpchmm.region (r_regionkey); TPC-H Q UERYS Q U E RY 2 SELECT s_acctbal, s_name, n_name, p_partkey, p_mfgr, s_address, s_phone, s_comment FROM part, supplier, partsupp, nation, region WHERE p_partkey = ps_partkey AND s_suppkey = ps_suppkey AND p_size = 15 AND p_type like '%BRASS' AND s_nationkey = n_nationkey AND n_regionkey = r_regionkey AND r_name = 'EUROPE' AND ps_supplycost = (SELECT MIN(ps_supplycost) FROM partsupp, supplier, nation, region WHERE p_partkey = ps_partkey AND s_suppkey = ps_suppkey AND s_nationkey = n_nationkey 17 AND n_regionkey = r_regionkey AND r_name = 'EUROPE') ORDER BY s_acctbal desc, n_name, s_name, p_partkey; Q U ER Y 3 SELECT l_orderkey, SUM(l_extendedprice*(1-l_discount)) as revenue, o_orderdate, o_shippriority FROM customer, orders, lineitem WHERE c_mktsegment = 'BUILDING' AND c_custkey = o_custkey AND l_orderkey = o_orderkey AND o_orderdate < '15-MAR-1995' AND l_shipdate > '15-MAR-1995' GROUP BY l_orderkey, o_orderdate, o_shippriority ORDER BY revenue desc, o_orderdate; Q U E RY 6 SELECT SUM(L_EXTENDEDPRICE * L_DISCOUNT) AS REVENUE FROM LINEITEM WHERE L_SHIPDATE >= TO_DATE(’1997-01-01’,’YYYY-MM-DD’) AND L_SHIPDATE < ADD_MONTHS(TO_DATE(’1997-01-01’,’YYYYMMDD’),12) AND L_DISCOUNT BETWEEN 0.05 - 0.01 AND 0.05 + 0.01 AND L_QUANTITY < 24 Q U E RY 9 SELECT NATION, YEAR, SUM(AMOUNT) AS SUM_PROFIT FROM (SELECT N_NAME AS NATION, TO_CHAR(O_ORDERDATE,’YYYY’) AS YEAR, L_EXTENDEDPRICE * (1-L_DISCOUNT) - PS_SUPPLYCOST * L_QUANTITY AS AMOUNT FROM PARTS, SUPPLIER, LINEITEM, PARTSUPP, ORDERS, NATION WHERE S_SUPPKEY = L_SUPPKEY AND PS_SUPPKEY = L_SUPPKEY AND PS_PARTKEY = L_PARTKEY AND P_PARTKEY = L_PARTKEY AND O_ORDERKEY = L_ORDERKEY AND S_NATIONKEY = N_NATIONKEY 18 AND P_NAME LIKE ’%snow%’ ) PROFIT GROUP BY NATION, YEAR ORDER BY NATION, YEAR DESC Q U ER Y 14 select 100.00 * sum(case when p_type like 'PROMO%' then l_extendedprice * (1 - l_discount) else 0 end) / sum(l_extendedprice * (1 - l_discount)) as promo_revenue from lineitem, part where l_partkey = p_partkey and l_shipdate >= date '1998-12-01' and l_shipdate < date '1998-12-01' + interval '1' month; Q U ER Y 15 create view revenues (supplier_no, total_revenue) as select l_suppkey, sum(l_extendedprice * (1 - l_discount)) from lineitem where l_shipdate >= date '1999-12-01' and l_shipdate < date '1999-12-01' + interval '3' month group by l_suppkey; select s_suppkey, s_name, s_address, s_phone, total_revenue from supplier , revenues where s_suppkey = supplier_no and total_revenue = ( select max(total_revenue) from revenues 19 ) order by s_suppkey; drop view revenues; Q U ER Y 18 select c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice, sum(l_quantity) from customer, orders, lineitem where o_orderkey in ( select l_orderkey from lineitem group by l_orderkey having sum(l_quantity) > 15 ) and c_custkey = o_custkey and o_orderkey = l_orderkey group by c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice order by o_totalprice desc, o_orderdate; F RAMWORK IN J AV A C L A SS B E N CH M AR K T E S T . JAV A import java.util.*; public class BenchmarkTest { @SuppressWarnings("unused") private List<Query> turnOns; @SuppressWarnings("unused") private List<Query> turnOffs; @SuppressWarnings("unused") private Query query; private String name; 20 public BenchmarkTest(String name, String query, String[] ons, String[] offs) { this.name = name; this.query = new Query(query); this.turnOffs = new ArrayList<Query>(); this.turnOns = new ArrayList<Query>(); /* add the tuning parameters to a query list */ for (String turnOn : ons) { Query bla = new Query(turnOn); this.turnOns.add(bla); } for (String turnOff : offs) { this.turnOffs.add(new Query(turnOff)); } } public void bench() { this.benchNoOpt(); this.benchOpt(); } private void benchNoOpt() { // run unoptimized query: for (Query turnOff : this.turnOffs) { // turn all optimizations off turnOff.run(); } this.startTrace(this.name+"_no_opt"); // run the actual query this.query.run(); this.stopTrace(); System.out.println("-- Query " + this.name + " without optimization ran..."); } private void benchOpt() { // run unoptimized query: for (Query turnOn : this.turnOns) { // turn all optimizations off turnOn.run(); } // set the new name for the tracefile // run the actual query this.startTrace(this.name+"_opt"); // run the actual query this.query.run(); this.stopTrace(); System.out.println("-- Query " + this.name + " with optimization ran..."); } private void stopTrace() { Query b = new Query("ALTER SYSTEM SET TIMED_STATISTICS=false"); b.run(); Query c = new Query("ALTER SESSION SET SQL_TRACE=false"); c.run(); return; } 21 private void startTrace(String id) { String[] timerOffString = { "ALTER SESSION SET TRACEFILE_IDENTIFIER='" + id + "'", "ALTER SYSTEM SET TIMED_STATISTICS=TRUE", "ALTER SESSION SET SQL_TRACE=TRUE" }; List<Query> timer = new ArrayList<Query>(); for (String off : timerOffString) { timer.add(new Query(off)); } for (Query time : timer) { time.run(); } return; } } C L A SS Q U E RY . JAV A import import import import java.sql.DriverManager; java.sql.SQLException; java.sql.Connection; java.sql.Statement; public class Query { @SuppressWarnings("unused") private String query; String url = "jdbc:oracle:thin:[email protected]:1521/XE"; static Connection con; Statement stmt; public Query(String query) { this.query = query; } @SuppressWarnings("unused") public String getQuery() { return this.query; } public void run() { System.out.println("Query: " + this.query); try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (java.lang.ClassNotFoundException e) { System.err.print("ClassNotFoundException: "); System.err.println(e.getMessage()); } try { if (con == null) con = DriverManager.getConnection(url, "tpch", "tpch"); stmt = con.createStatement(); stmt.executeQuery(this.query); } catch (SQLException e) { System.err.println(e.getMessage()); 22 } } public static void close() { try { if (!con.isClosed()) con.close(); } catch (SQLException e) { // TODO Auto-generated catch block System.err.println(e.getMessage()); } } } C L A SS Q U E RY L O A D ER . J AV A public class QueryLoader { public static void main(String args[]) { final String query1 = "SELECT DISTINCT a.artist,al.album,s.song, genre from genres g,artists a,cds c, " + "cdtracks ct, songs s, artist2album a2a, albums al WHERE (a.artist LIKE 'ar%') AND " + "a.artistid = a2a.artistid AND a2a.albumid = al.albumid AND " + "a2a.artist2albumid = c.artist2albumid AND ct.cdid = c.cdid " + "AND g.genreid = c.genreid AND ct.songid = s.songid"; final String[] on = { "CREATE INDEX artistsid on cddb.artists (artistid)", "CREATE INDEX artistname on cddb.artists (artist)", "CREATE UNIQUE INDEX genreid on cddb.genres (genreid)", "CREATE INDEX cgenreid on cddb.cds (genreid)", "CREATE INDEX ctcdid on cddb.cdtracks (cdid)", "CREATE INDEX cda2a on cddb.cds (artist2albumid)", "CREATE UNIQUE INDEX a2albumid on cddb.artist2album (artist2albumid)", "CREATE INDEX ctsid on cddb.cdtracks (songid)", "CREATE INDEX songid on cddb.songs (songid)", "ALTER TABLE albums add CONSTRAINT albumid PRIMARY KEY (albumid)" }; final String[] off = { "DROP INDEX artistsid ", "DROP INDEX artistname ", "DROP INDEX cgenreid ", "DROP INDEX genreid", "DROP INDEX cda2a", "DROP INDEX ctcdid", "DROP INDEX a2albumid", "DROP INDEX ctsid", "DROP INDEX songid", "ALTER TABLE albums drop CONSTRAINT albumid" }; BenchmarkTest test = new BenchmarkTest("q1", query1, on, off); test.bench(); Query.close(); } } 23 B IBLIOGRAPHIE [1] TPC Website, Stand 24.09.07, http://www.tpc.org/tpch/ [2] OTN Download Site, Stand 24.09.07, http://www.oracle.com/technology/software/products/database/xe/htdocs/102xelinsoft.html [3] CDDB Gracenote, Stand 25.09.07, http://www.gracenote.com/music/ [4] Oracle Datatypes Tech on the Net, Stand 25.09.07, http://www.techonthenet.com/oracle/datatypes.php [5] PostgreSQL Datatypes PostgreSQL Manual, Stand 25.09.07, http://www.postgresql.org/docs/7.3/interactive/datatype.html [6] Oracle FAQ, Stand 25.09.07, http://www.orafaq.com/faqloadr.htm [7] Oracle Scratchpad Blog, 1.10.07, http://jonathanlewis.wordpress.com/2007/02/21/ignoring-hints/ 24