Einleitung Einleitung W ie und w as w urde getestet W ie
Transcription
Einleitung Einleitung W ie und w as w urde getestet W ie
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