Einleitung Einleitung W ie und w as w urde getestet W ie

Werbung
Einleitung
Page 1 of 7
Betrifft: Java oder PL/SQL?
Art der Info: Technische Background Info
Autor: Guido Schmutz ([email protected])
Quelle: Aus unserer Schulungs- und Beratungstätigkeit
Mit Oracle8.1 besteht neu die Möglichkeit, neben der Sprache PL/SQL auch Java zum Entwickeln
von serverseitigem Code zu verwenden. Java-Code wird wie PL/SQL in der Datenbank gespeichert
und ausgeführt.
Oracle wird die zwei Sprachen parallel weiterentwickeln und viele von uns werden in Zukunft beide
Sprachen verwenden, um Datenbank-Applikationen zu entwickeln. Die Kernfrage lautet deshalb:
Wann soll welche Sprache eingesetzt werden ?
Oracle selbst positioniert PL/SQL als robuste, prozedurale Datenbank-Sprache. Java wird als die
Sprache für die Implementierung von Komponenten bezeichnet. Ein wichtiges Kriterium, um
entscheiden zu können, ob Java oder PL/SQL verwendet werden soll, ist bei serverseitigen
Applikationen sicher die Performance von Datenzugriffen.
Im Rahmen der Vorbereitung unserer neuen Kurse „Java-DB“ und „PL/SQL-B“ haben wir einen
Performance-Test mit beiden Sprachen durchgeführt. Dieser Artikel präsentiert einen Ausschnitt
dieser Resultate und soll aufzeigen, was von Java und PL/SQL bezüglich Performance erwartet
werden kann.
Getestet haben wir bei beiden Sprachen eine SELECT und eine INSERT Operation jeweils auf
unterschiedlichen Datenmengen (100, 500, 1'000, 10'000, 50'000 und 100'000 Rows). Jeder einzelne
Test wurde 7 mal wiederholt, und es wurde immer nur das beste Resultat der 7 Versuche in die
Auswertung übernommen.
Die Tests wurden auf einer Oracle8.1.5 Datenbank jeweils auf einem NT Server, einmal mit 1, 2,
und 4 CPU’s vorgenommen. Die hier gezeigten Resultate stammen von der 4 CPU Maschine mit
256M Memory. Es wurden alle neuen Features von Oracle 8.1.5 verwendet, insbesondere für
PL/SQL gibt es einige neue Features, welche die Performance wesentlich verbessern.
Es gibt sowohl mit PL/SQL wie auch mit JAVA mehrere Varianten, um die SELECT- bzw.
INSERT-Operationen auszuführen. In diesem Artikel beschränken wir uns auf die jeweils beste und
die schlechteste Lösung für beide Sprachen.
Folgendes PL/SQL Codefragment zeigt das grundlegende Statement für diesen Test:
FOR rec IN (SELECT person_id, name, vorname, ... FROM person)
LOOP
tab(i) := rec;
i = i
END LOOP;
Java
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Einleitung
Page 2 of 7
Als beste Variante hat sich der SELECT über JDBC erwiesen, wobei das schnellste Resultat mittels
prepareStatement und der Oracle-spezifische JDBC-Methode defineColumnType
erreicht wurde.
PreaparedStatement stmt =
con.prepareStatement ("SELECT person_id, name, vorname,... FROM person");
((OraclePreparedStatement)stmt).defineColumnType (1, Types.INTEGER);
((OraclePreparedStatement)stmt).defineColumnType (2, Types.VARCHAR2);
...
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
...
}
Die schlechtesten Resultate ergaben sich mit der Verwendung von JSQL. Bei JSQL werden die SQL
Befehle über embedded SQL in den Java Code aufgenommen und von einem Precompiler übersetzt.
#sql public static iterator SelectTestIter (int person_id, String name...);
...
#sql rs = {SELECT person_id, name, voranme, ... FROM person};
while (rs.next()) {
...
}
PL/SQL
Bei PL/SQL wurden die besten Resultate mit der neuen PL/SQL8.1 Bulk-Operation erreicht. Beim
SELECT wird das Bulk-Binding mit dem Schlüsselwort BULK COLLECT INTO ausgelöst.
OPEN c_per FOR SELECT person_id, name, vorname, ... FROM person;
FETCH c_per BULK COLLECT INTO person_id_tab, name_tab, vorname_tab, ...;
CLOSE c_per;
Die schlechteste Performance bringt die Verwendung von dynamischem SQL mit dem Package
DBMS_SQL.
cur := DBMS_SQL.open_cursor();
stmt := 'SELECT person_id, name, vorname, ... FROM person';
DBMS_SQL.parse (cur, stmt, DBMS_SQL.native);
DBMS_SQL.define_column (cur, 1, rec.person_id);
...
ret := DBMS_SQL.execute (cur);
WHILE (DBMS_SQL.fetch_rows (cur) <> 0)
LOOP
DBMS_SQL.column_value (cur, 1, rec.person_id);
...
END LOOP;
Folgendes PL/SQL Codefragment zeigt das grundlegende Statement für diesen Test:
FOR i IN 1 .. tab.COUNT()
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Einleitung
Page 3 of 7
LOOP
INSERT INTO person VALUES (person_id_tab(i), name_tab(i), ...);
i := i + 1;
END LOOP;
Java
Als beste Variante hat sich der INSERT über JDBC erwiesen, wobei das schnellste Resultat mit der
Verwendung der Oracle-spezifischen setExecuteBatch Funktionalität erreicht wurde. Dabei
werden einzelne INSERT Befehle zuammengefasst und mittels Bulk-Operation in die Datenbank
geschrieben. Vorbereitet wurde das Statement auch hier mit der prepareStatement Methode
und zusätzlich wurden Bind-Variablen verwendet, die jeweils im Insert-Loop mit den
entsprechenden Werten bestückt werden.
PreaparedStatement stmt =
con.prepareStatement ("INSERT INTO person VALUES (?, ?, ?, ... ");
((OraclePreparedStatement)stmt).setExecuteBatch(count)
...
for (int i=0; i<count; i++) {
stmt.setInt (1, person_id);
stmt.setString (2, name);
...
}
Die schlechtesten Resultate ergaben sich hier nicht mit JSQL, sondern mit Standard-JDBC, d.h. ohne
die obengezeigten Optionen. Zudem wurde das Statement jeweils im Loop, analog zum PL/SQL
Grundcode, aufgebaut und abgesetzt, aber ohne Verwendung von Bind-Variablen.
for (int i=0; i<count; i++) {
Statement stmt = con.createStatement();
String sql =
"INSERT INTO person VALUES ( " + person_id + ", " + name + ", " + ...;
stmt.executeUpdate(sql);
stmt.close();
}
PL/SQL
Bei PL/SQL werden wiederum die besten Resultate mit der neuen PL/SQL8.1 Bulk-Operation
erreicht. Beim INSERT wird dies mit dem Schlüsselwort FORALL ausgelöst.
FORALL I IN person_id_tab.FIRST() .. person_id_tab.LAST()
INSERT INTO person VALUES person_id_tab(i),name_tab(i),vorname_tab(i)...;
Die schlechteste Performance bringt auch hier, gleich wie beim SELECT, die Verwendung von
dynamischem SQL über das Package DBMS_SQL. Dies obwohl das Statement optimiert
abgearbeitet wird, d.h. nur einmal geparsed wird und Bind-Variablen eingesetzt werden.
cur := DBMS_SQL.open_cursor();
stmt := 'INSERT INTO person VALUES (:person_id, :name, :vorname, ...');
DBMS_SQL.parse (cur, stmt, DBMS_SQL.native);
FOR i IN 1 .. count
LOOP
DBMS_SQL.bind_variable (cur, 'person_id', person_id);
...
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Einleitung
Page 4 of 7
ret = DBMS_SQL.execute (cur);
END LOOP;
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Einleitung
Page 5 of 7
Die folgenden Graphiken zeigen die Resultate der einzelnen Tests. Es wurden jeweils die
schlechteste Lösung einer Sprache der besten Lösung der anderen Sprache gegenübergestellt und
umgekehrt. Die Lösungen entsprechen den vorgängig besprochenen Varianten.
Es sind nur die Resultate der Test mit Datenmenge 10'000, 50'000 und 100'000 Rows gezeigt. Die
Resultate mit den kleineren Datenmengen ergeben jedoch das gleiche Bild.
Die Y-Achse zeigt die Ausführungszeit in Sekunden, die X-Achse die Anzahl Rows. Das JavaResultat ist als Balken dargestellt, das PL/SQL-Resultat als Linie.
Beste Java vs. schlechteste PL/SQL Variante für SELECT
60
49.4
51.1
50
40
30
jdbc_sel_defined
24.81
25.45
plsql_sel_dbms_sql
20
10
4.98
5.14
0
ROWS_10000
ROWS_50000 ROWS_100000
Schlechteste Java vs. beste PL/SQL Variante für SELECT
70
57.31
60
50
40
sqlj_sel
28.59
30
plsql_sel_bulk_collect
20
10
0
5.7
0.53
ROWS_10000
8.67
2.59
ROWS_50000 ROWS_100000
Die Resultate zeigen, dass die schnellste Java-Lösung für den SELECT nur geringfügig schneller ist,
als die schlechteste PL/SQL-Lösung. Die beste PL/SQL-Lösung ist die absolut schnellste Variante,
sie ist bis zu 10x schneller als die Java-Lösung. Die beste und die schlechteste Java-Variante
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Einleitung
Page 6 of 7
unterscheiden sich nur wenig.
Es muss bei diesem Vergleich berücksichtigt werden, dass die schnellste PL/SQL-Lösung nur mit
statischem SQL (SQL-Statement muss bereits zur Kompilierungszeit bekannt sein) erreicht werden
kann, Java hingegen immer mit dynamischem SQL arbeitet. Die schnellste Variante für ein
dynamisches SELECT in PL/SQL (DBMS_SQL mit Array-Binding) ergibt für 50'000 Rows eine
Zeit von 12.22 Sekunden, was aber immer noch 2x schneller ist als Java.
Beste Java vs. schlechteste PL/SQL Variante für INSERT
350
310.73
300
250
200
jdbc_ins_batched
157.58
150
90.6
100
50
plsql_ins_dbms_sql
44.4
8.130.9
0
ROWS_10000
ROWS_50000 ROWS_100000
Schlechteste Java vs. beste PL/SQL Variante für INSERT
800
720.6
700
600
500
jdbc_ins
348.62
400
plsql_ins_forall
300
200
100
0
65.58
5.35
ROWS_10000
36.66
77.56
ROWS_50000 ROWS_100000
Die Resultate beim INSERT zeigen ein anderes Bild als beim SELECT. Hier ist die beste JavaLösung viel schneller, als die schlechteste PL/SQL-Lösung (mehr als 3x schneller). Die optimalste
PL/SQL-Variante ist auch hier besser als jede Java-Lösung, der Unterschied zur besten Java-Lösung
ist aber viel kleiner als beim SELECT.
Der Unterschied zwischen der besten und der schlechtesten Java-Lösung ist hier viel grösser.
Auch beim INSERT muss berücksichtigt werden, dass die beste PL/SQL-Variante wiederum nur
über statisches SQL erreicht werden kann. Die schnellste Variante für einen dynamischen INSERT
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Einleitung
Page 7 of 7
mit PL/SQL (BULK COLLECT INTO über Native Dynamic SQL) ergibt für 50'000 Rows eine Zeit
von 46.2 Sekunden, was praktisch identisch ist mit der besten Java-Variante.
Die Performance-Tests zeigen wie wichtig es ist, die jeweiligen Features der Sprache und deren
Eigenschaften genau zu kennen. Sowohl mit PL/SQL wie auch mit Java können LaufzeitUnterschiede von mehreren Faktoren (5x – 10x) erreicht werden. Es kann aber nicht immer die beste
Variante in der Praxis auch wirklich eingesetzt werden. Gerade bei PL/SQL sind die besten
Varianten immer gekoppelt mit statischem SQL.
Uns hat überrascht, wie gut Java bei diesen ersten Tests abschneidet. Insbesondere beim INSERT
sind die Unterschiede zu PL/SQL nur noch gering bzw. nicht vorhanden (bei dynamischem SQL).
Beim SELECT sieht es nicht ganz so gut aus, es kann aber erwartet werden, dass in einem nächsten
Release auch hier Verbesserungen folgen werden (insbesondere bei der Variante mit SQLJ).
Falls Sie den Code zu den Performance-Test wünschen, schreiben Sie mir bitte ein Mail und ich
werde Ihnen diesen zusenden.
Gute Performance mit PL/SQL und/oder Java wünscht
Trivadis AG
Guido Schmutz
Papiermühlestrasse 159
CH 3063 Ittigen b. Bern
Tel:
+41 31 928 09 50
Fax:
+41 31 928 09 51
http://www.trivadis.com/Images/JavaPerf_tcm16-7133.htm
15.09.2004
Herunterladen