28. April 2008

Geodaten LIVE erleben: Workshops ...

Geodaten werden (wie jeder jeden Tag im Internet beobachten kann) immer wichtiger. Dienste wie Google Earth und Google Maps kennt wohl jedermann - und so stellt sich die Frage, ob man nicht auch (datenbankbasierte) Geschäftsanwendungen mit Geo-Funktionalität ausstatten kann. Und das ist weniger aufwändig als man denkt - so kann eine Karte in 15 Minuten in einer Application Express-Anwendung eingebunden werden.
Mehr Informationen ...?

In nächster Zeit finden zum Thema zwei Veranstaltungen statt - die Teilnahme ist in beiden Fällen kostenlos ...
  • Am 14. Mai 2008 findet in Darmstadt eine gemeinsame Veranstaltung mit dem InGeoForum statt. Thema ist Oracle MAPS und dessen praktischer Einsatz. Hier finden zwar keine Hands-On statt - dafür wird der Einsatz der Oracle-Geodaten-Technologie anhand praktischer Projekte gezeigt.
    [Mehr Informationen hier]

  • Am 8. Mai 2008 findet ein Hands-On Workshop "Geodaten LIVE erleben" in der Oracle-Geschäftsstelle in München statt. Hier könnt Ihr euren Laptop mitbringen und die Oracle-Geodaten-Technologie direkt selbst ausprobieren ...
    [Anmeldung hier]
Vielleicht sehen wir uns auf einer der Veranstaltungen ... Noch sind Plätze frei ...
- mehr und mehr Anwendungen werden mit zusätzlicher Geo-Funktionalität ausgestattet.

25. April 2008

Datenbank-Sessions synchronisieren: DBMS_ALERT

English title: Synchronizing database sessions: DBMS_ALERT

Die Oracle-Datenbank kommt bekanntlich mit einer Menge PL/SQL-Pakete daher und es ist fast unmöglich, immer mit allen Neuerungen Schritt zu halten. Nun ist mir mal wieder ein Kandidat untergekommen, der so kaum bekannt ist: DBMS_ALERT. Mit DBMS_ALERT können Datenbank-Sessions sich untereinander durch das Versenden und Empfangen von Events synchronisieren. Eine Session kann sich für bestimmte Ereignisse registrieren und dann auf das Auftreten eines Ereignisses warten ... Sobald das Ereignis eintritt, geht die Verarbeitung weiter. DBMS_ALERT basiert auf dem "normalen" Transaktionsverhalten der Datenbank - es nutzt "normale" Locks zum Synchnonisieren der Sessions und man muss das Senden eines Ereignis auch durch ein COMMIT bestätigen (DBMS_PIPE wäre eine nicht-Transaktionale Alternative).
It is very well known that the Oracle database has a very rich function libary and it's kind of difficult to keep track with all the new PL/SQL packages and functions. Now I've seen a candidate which is not very common and I like to write a few lines about it. DBMS_ALERT allows to synchronize different database sessions using the "normal" database transaction mechanism. A session may register for an event and after this the session can wait for the occurance of this event. If another session signals the event the first session awakes and proceeds its opperation. As the database transaction mechanism is used the signalling has to be committed (as we'll see). If you're looking for a non-transactional alternative, have a look into DBMS_PIPE.
Probieren wir das mal aus: Dazu brauchen wir zwei Datenbank-Sessions - es muss nicht unbedingt der gleiche Datenbankuser verwendet werden. Da das DBMS_ALERT-Paket dem SYS-User gehört, müssen Sie vor ggfs. EXECUTE-Privilegien daran vergeben. Starten Sie anschließend zwei Datenbank-Sessions (am besten mit SQL*Plus).
Let's have a try on that. All we need is two database sessions (can be different database users) and EXECUTE privilege on DBMS_ALERT (this is owned by SYS). This example uses SQL*Plus for the two database sessions.
Session 1 ist die Session, die auf eine andere warten soll - daher muss sie sich zuerst als "Interessent" für ein Ereignis registrieren:
Session 1 should wait for the event - so it must subscribe to this event by using DBMS_ALERT.REGISTER:
begin
 dbms_alert.register('TESTEVENT');
end;
/
Anschließend warten wir auf das Ereignis mit DBMS_ALERT.WAITONE. Diese Prozedur nimmt zunächst den Namen des Ereignisses auf das gewartet werden soll, auf. Die nächsten zwei OUT-Parameter (message und status geben zusätzliche Informationen, wenn die Prozedur "zurückkehrt". DBMS_ALERT.WAITONE kehrt zurück, wenn entweder das Ereignis auftritt oder der Timeout abläuft. Der OUT-Parameter status enthält dann den Wert 0 wenn das Ereignis aufgertreten ist und 1 wenn der Timeout abgelaufen ist. Gibt man keinen Parameter timeout an, so wartet die Prozedur auf jeden Fall solange, bis das Ereignis auftritt. In diesem Beispiel nutzen wir zwei SQL*Plus-Variablen ("msg" und "status") - darin werden die Werte der bneiden OUT-Parameter message und status abgelegt. (Wichtig: Dieser Aufruf löst ein implizites COMMIT aus)...
Next the session is going to wait for the occurrance of TESTEVENT. It uses DBMS_ALERT.WAITONE for this. This procedure takes the name of the event to wait for as the first parameter. The next two OUT-Parameters message and status give event information when the procedure returns. DBMS_WAITONE returns either when the event was being signalled or when the timeout occured. The OUT parameter status contains 0 if the session received the event and 1 if the procedure just timed out. If the parameter tomeout is not specified the procedure waits forever. In this example we define two SQL*Plus variables ("msg" and "status") to receive the OUT parameters message and status. (Important: The procedure issues an implicit COMMIT) ...
var msg     varchar2(200);
var status  number;

begin
 dbms_alert.waitone(
   name    => 'TESTEVENT',
   message => :msg,
   status  => :status
 );
end;
/
... und nun wartet die Session ... und zwar in diesem Beispiel solange, bis von einer anderen Session aus das Ereignis TESTEVENT ausgelöst wird. Also neue Datenbanksession aufmachen und folgendes absetzen:
... and now this session will wait ... in this example until TESTEVENT will be signalled from another session. So we open Session 2 and issue the following:
begin
 dbms_alert.signal(
   name    => 'TESTEVENT',
   message => 'This is a message / Dies ist eine Nachricht'
 );
end;
/
Nun passiert zunächst noch nichts ... wenn aber dann ein COMMIT abgesetzt wird, erwacht die Session 1 wieder zum Leben ...
Seems not to work ... Session 1 is still waiting - but after issuing COMMIT from Session 2 the first Session 1 wakes up and is alive.
:
~~~ wait ~~~
:

PL/SQL-Prozedur erfolgreich abgeschlossen.

Abgelaufen: 00:00:06.39
Die Inhalte der Bind-Variablen "msg" und "status" kann man mit dem SQL*Plus-Kommandi print anzeigen lassen:
Let's have a look at the SQL*Plus variables "msg" and "status" using the print command:
SQL> print

MSG
-------------------------------------------
This is a message / Dies ist eine Nachricht


    STATUS
----------
         0
Neben der Prozedur WAITONE gibt es noch WAITANY. Der Name des Ereignis ist dann ein OUT-Parameter, da WAITANY auf ein beliebiges Ereignis (die Session muss sich lediglich registriert haben) wartet. Wenn ein Ereignis eingetreten ist, wird dessen Name im Out-Parameter name zurückgegeben.
Besides WAITONE there is the WAITANY procedure. As its name indicates this procedure waits for any event the session has registered to. So we don't provide a name for the event to wait for - the parameter name is now an OUT parameter providing the signalled event instead.
Wichtig ist übrigens das COMMIT nach dem Aufruf von SIGNAL. Grund ist, dass pro Ereignis alle Aufrufe von SIGNAL serialisiert werden. Setzt also eine andere Session ebenfalls ein SIGNAL für das gleiche Ereignis ab, wartet diese solange, bis der erste Aufruf von SIGNAL committed wurde.
The COMMIT after the SIGNAL call is quite important. The reason is that for one event (here: TESTEVENT) all SIGNAL calls will be serialized. A second SIGNAL call for TESTEVENT will therefore wait until the first one is being committed. As said in the beginning: DBMS_ALERT is transactional.
Bleibt noch das Aufräumen. Mit DBMS_ALERT.REMOVE entfernt eine Session ihre Registrierung für ein Ereignis wieder ...
So we only have to cleanup now. A session unsubscribes from an event using the DBMS_ALERT.REMOVE call ...

17. April 2008

Mengenoperationen vs. Einzelzugriffe

English title: Singe row vs. bulk operations

Heute starten wir mal mit einer ganz einfachen Anforderung: Wir möchten eine Tabellenzeile aufsummieren. Dazu habe ich mal vier Ansätze vorbereitet ... die findet Ihr im Folgenden (und es soll keiner sagen, manche Ansätze wären total unrealistisch und keiner würde so programmieren - alles schon gesehen). Die Tabelle MYSALES ist ein Subset (bei mir mit 90.000 Zeilen) der Tabelle SH.SALES aus den Oracle Sample Schemas - kann man sich einfach mit einem CTAS und der SAMPLE-Klausel erzeugen ...
Today we'll start with a very simple requirement: We want to sum up a table column. I've prepared four approaches for this ... and I've seen examples for every introduced approach. Be careful with saying "nobody writes code like this" - the world is big. The table MYSALES is a subset (90.000 rows) of the table SH.SALES in the Oracle Sample Schemas. You can create it easily with a CTAS and the SAMPLE clause ...
Lasst das Skript einfach mal laufen und schaut euch die Unterschiede an ...
Just start the script and have a look at the differences ...
set serveroutput on
set timing on

/* 1. Ansatz */

declare
  v_sum number := 0.0;
  v_val number;
begin
  /*
   * ROWID-Zugriffe, da ROWID der schnellste denkbare Zugriff
   * auf eine Tabellenzeile ist
   */
  for i in (select rowid from mysales) loop
    select amount_sold into v_val from mysales where rowid=i.rowid;
    v_sum := v_sum + v_val;
  end loop;
  dbms_output.put_line(v_sum);
end;
/

19662136,24   

PL/SQL procedure successfully completed.

Elapsed: 00:00:12.55

/* 2. Ansatz */

declare
  v_sum number := 0.0;
begin
  for i in (select amount_sold from mysales) loop
    v_sum := v_sum + i.amount_sold;
  end loop;
  dbms_output.put_line(v_sum);
end;
/

19662136,24  

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.31

/* 3. Ansatz */

declare
  type numlist_t is table of number index by binary_integer;
  v_val numlist_t;

  v_sum number := 0.0;
begin
  select amount_sold bulk collect into v_val from mysales;
  for i in 1..v_val.count loop
    v_sum := v_sum + v_val(i);
  end loop;
  dbms_output.put_line(v_sum);
end;
/

19662136,24   

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.17

/* 4. Ansatz */

select sum(amount_sold) from mysales
/

SUM(AMOUNT_SOLD)
----------------
     19662136,24  

1 row selected

Elapsed: 00:00:00.04
Man sieht, dass der erste Ansatz der langsamste ist. Um das Argument der zusätzlichen Indexzugriffe von vorneherein auszuschließen, habe ich den Zugriff über die ROWID gemacht. Wenn die Schleife also durchläuft, wird auf jede Tabellenzeile direkt und ohne Umwege zugegriffen. Trotzdem hat er 12 Sekunden gebraucht: Die Datenbank musste das SQL ...
It's obvious that the first approach is the slowest one. To bypass the dicussion about index access I used ROWID's to access the individual table rows. Therefore the loop accesses every table row directly and without using an index or any other additional I/O. Nevertheless, it took 12 seconds: The database had to execute the SQL query ...
select amount_sold from mysales where rowid = :1
... ja auch 90.000 mal ausführen. Auch wenn man ROWID's und Bindevariablen nutzt, braucht das immer noch seine Zeit. Schauen wir uns die zweite Variante an:
... 90.000 times. Although ROWID's and bind variables were used this took some time. Let's have a look at the second approach:
Hier haben wir schon eine Mengenoperation, lediglich das SUM machen wir noch mit PL/SQL. Und da nur noch ein SQL-Kommando ausgeführt wird, geht das auch schon wesentlich schneller: Nur noch eine Drittelsekunde. Zwar wurde in diesem Fall nur noch eine SQL-Abfrage ausgeführt, allerdings wurden immer noch 90.000 Tabellenzeilen in den PL/SQL Block "gefetcht" - da es eine "ordinäre" PL/SQL Schleife ist, bedeutet dies nochmals 90.000 "Round-Trips" von SQL nach PL/SQL. Diese Roundtrips sind einzeln betrachtet zwar nicht teuer, aber schauen wir mal auf Variante 3:
Here we have a mass operation, just the summing-up is done within PL/SQL. And since the SQL query is executed only once this is very much faster than the first approach. But although the SQL Query was executed only once the PL/SQL loop had to fetch all the 90.000 rows. And the "ordinary" PL/SQL loop did this row-by-row, so we had 90.000 context switches between SQL and PL/SQL. A single context switch is not very expensive, but if you consider it 90.000 times ... let's look at approach 3:
... in welcher die PL/SQL Loop durch eine BULK COLLECT-Anweisung ersetzt wurde. Es werden immer noch 90.000 Zeilen von SQL nach PL/SQL übertragen, aber mit nur noch einem Round-Trip zwischen SQL und PL/SQL. Durch die lange Zeit, die Variante 1 verbraucht hat, geht dieser Effekt fast unter ... aber es ist fast Faktor 2 (0,17 Sekunden vs. 0,31 Sekunden). Das dies kein zufälliger Effekt ist, lässt sich nachweisen, wenn man die Varianten 2 und 3 mal gegen die Originaltabelle SH.SALES laufen lässt ...
... which used the BULK COLLECT clause. So the 90.000 rows are still being fetched from SQL to PL/SQL but this requires only one roundtrip. Due to the long elapsed time of approach 1 the difference between approaches 2 and 3 seems to be not significant - but it is nearly factor 2 (0,17 vs. 0,31 seconds). Testing both approaches with the bigger "original table" SH.SALES reveals that this difference is not by coincidence:
SQL> start script2.sql
Variante 2: 98205831,21

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.88

Variante 3: 98205831,21

PL/SQL procedure successfully completed.

Elapsed: 00:00:01.09
Der Bulk Collect macht diese Operation also um 40% schneller. Was kann noch besser sein?
As seen: Using the bulk collect clause leads to a 40% faster operation - what could be better ...?
Ganz klar: Für diese Aufgabe(!) ganz auf PL/SQL verzichten Denn eine Summierung (und so hätten wir es alle von vorneherein gemacht) kann direkt im SQL ausgeführt werden. Das ist mit 0.04 Sekunden (MYSALES) bzw. 0,36 Sekunden (auf der Originaltabelle) die schnellste Variante.
For this requirement this is easy: Do it within pure SQL! PL/SQL is not needed for just summing up a column - and most of us wouldn't have used it. So the "pure" SQL approach is with 0,04 seconds also the fastest one. The operation on the "original table" SH.SALES took 0,36 seconds which is also faster than the other approaches.
Was kann man also daraus ableiten? Zunächst ist es eine Bestätigung des alten, bekannten Grundsatzes "80% des Tuning-Erfolges lassen sich durch Anwendungstuning erzielen". Meint also: Bevor man bei langsam laufenden PL/SQL-Blöcken ins Systemtuning (Datenbankparameter, I/O Tuning, etc) einsteigt, sollte man stets prüfen, ob der Code seine Aufgabe optimal erfüllt - und dabei kann man die vier hier vorgestellten Varianten quasi rückqärts abarbeiten:
  1. Lässt sich die Aufgabe mit reinem SQL erledigen?
    Filtern, Sortieren, Gruppieren niemals mit PL/SQL programmieren, sondern stets durch SQL-Abfragen sicherstellen. Auch anspruchsvollere Aggregate wie gleitende Durchschnitte oder das "Aufsummieren" von Tabellenzeilen benötigen keinen PL/SQL-Code; dazu gibt es analytische Funktionen (einfach mal in die SQL Reference sehen). In PL/SQL sollte nur dann kodiert werden, wenn es wirklich nötig ist.
  2. Wenn PL/SQL programmiert wird: Niemals Einzelsatzzugriffe programmieren, wo eine Mengenoperation das gleiche erreichen kann..
    Insbesondere beim "Schachteln" von Cursorn sollte man aufpassen, ob man nicht gerade dabei ist, Einzelsatzzugriffe zu programmieren (kann man die gleiche Aufgabe nicht mit einem SQL und Subqueries erledigen). Selbst if-then-else-Konstrukte können mit purem SQL und dem CASE-Konstrukt erledigt werden.
  3. Bulk Collect bzw. Bulk DML nutzen, wo möglich.
    SQL und PL/SQL sind zwar "nahe Verwandte" aber doch getrennte Umgebungen. Wenn Werte aus einer SQL-Abfrage in eine PL/SQL-Variable überführt werden, findet ein sog. Context-Switch (oder "Roundtrip") zwischen SQL und PL/SQL statt. Der kostet (für sich betrachtet) nicht viel ... wird das in einer Schleife allerdings häufiger gemacht, so fällt es ins Gewicht. Bulk Collects übernehmen eine ganze Reihe von Werten auf einmal(!) in ein PL/SQL Array - das ist wesentlich schneller.
So what does this mean for practical development? Firstly this is the reassurance of the "old principle" that 80% of tuning success can be achieved by tuning the application. Expressed otherwise: Check the application code before tuning database parameters, I/O or similar - the effects is in most cases much better. And when doing this one can work reversely through the here introduced four approaches:
  1. Can the requirement be solved by using pure SQL?
    Filtering, ordering, grouping does not require PL/SQL coding - SQL is pretty good in doing this. Ambitious query requirements like moving averages, ratio-to-reports or moving sums can also be solved with pure SQL with analytical functions (just have a look into the SQL reference). Again: PL/SQL is not needed for this - use PL/SQL only when it is really necessary.
  2. In PL/SQL programs: Avoid accessing your tables row by row when you're indeed doing a mass operation. Be aware of this when nesting cursors (can't this be done with a single SQL and nested subqueries?). Remember that SQL even allows to code if-then-else blocks using the CASE constructs.
  3. Use bulk Collect or Bulk DML is possible.
    Though SQL and PL/SQL are "close relatives" they are separated environments. A context switch or roundtrip between SQL and PL/SQL is not very expensive, but if its done very often it gets significant. Coding an ordinary loop with "n" iterations means "n" context switches - using the bulk clauses means only one context switch - this leads to better performance in most cases.
Bulk-Operationen sind übrigens auch bei DML wesentlich schneller, wie das folgende Beispiel zeigt: 100.000 zufällige Werte sollen in eine Tabelle eingefügt werden:
BTW: Bulk operations are also faster when doing DML - as the following example illustrates: 100.000 random values are to be inserted in a table:
drop table random_values
/

create table random_values (col number)
/

declare
  v_val number;
begin
  for i in 1..100000 loop
    v_val := dbms_random.value(0,1000);
    insert into random_values values (v_val);
  end loop;
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:07.55

truncate table random_values
/

Tabelle mit TRUNCATE geleert.

Elapsed: 00:00:00.48

declare
  type numlist_t is table of number index by binary_integer;
  v_val numlist_t;
begin
  for i in 1..100000 loop
    v_val (i) := dbms_random.value(0,1000);
  end loop;
  forall i in 1..v_val.count 
    insert into random_values values (v_val(i));
end;
/

PL/SQL-Prozedur erfolgreich abgeschlossen.

Elapsed: 00:00:00.71
Fazit: Wir wussten es immer: Eine Datenbank kann ihre Fähigkeiten nur ausnutzen, wenn man sie lässt - das bedeutet, dass man der Datenbank als Entwickler die Ausführungspläne nicht vorschreibt (Einzelsatz!), sondern mengenorientiert arbeitet.
The database can do its job best only if the developer allows it. If application developers determine access paths in their application code the database optimizer cannot do much to improve performance. Its best practice to push mass operations completely down to the database.

7. April 2008

Schnellere Verbindungen zu Oracle11g: DBMS_CONNECTION_POOL

English title: Faster connections to Oracle11g: DBMS_CONNECTION_POOL)

Eine der weniger bekannten Eigenschaften von Oracle11g ist, dass sie einen neuen Connection-Pool mitbringt - und zwar den sogenannten Database Resident Connection Pool. Der Connection Pool wird also von der Datenbank selbst verwaltet. Die Frage liegt nahe, was das soll, denn Java oder .NET bringen bekanntlich ihre eigenen Connection Pools mit - wozu nun also einer in der Datenbank? Nun, primär wurde der Pool für PHP entwickelt - PHP ist von seiner Architektur her gar nicht in der Lage, einen eigenen Connection Pool zu haben - sobald ein PHP-Skript abgelaufen ist, werden alle Objekte zerstört. Um das Problem zu beheben, wird der Connection Pool von der Datenbank bereitgestellt ...
Oracle11g has brought many new features - some were dicussed very frequently and are therefore well known. Others are not so well-known - and one of these is the new Connection Pool: Database Resident Connection Pooling. This new connection pool is being maintained by the database engine itself. So people might ask, what's the purpose for this - since J2EE or .NET applications use their own connection pooling in most cases.
The answer is: PHP. This connection pool was primarily developed for PHP since PHP cannot have its own connection pool due to its architecture. When a PHP scripts ends all objects are destroyed - so a connection pool which would be shared by many PHP scripts cannot exist in the PHP architecture. Therefore the connection pool is provided by the database ...
Nun mögen manche einwerfen, dass das gar nicht neu ist - schließlich gibt es die Shared Server Architektur (auch als MTS - Multi Threaded Server bekannt) schon seit Oracle8i. Aber auch das wäre falsch, denn dieser neue Connection Pool enthält keine "shared" Server-Prozesse, welche mehrere Sessions bedienen können, sondern nur "dedicated" Server-Prozesse.
Perhaps this isn't new since Oracle has the Shared Server Architecture (also known as Multi-Threaded Server MTS) since Oracle8i. But in opposite to MTS this new connection pool contains dedicated, not shared server processes.
Was steckt also dahinter?
So what's behind this?
Der neue Database Resident Connection Pool dient dazu, "dedicated" Server-Prozesse "wiederzuverwenden" - wenn eine Datenbanksession also endet, wird der Server-Prozeß nicht zerstört, sondern nur "aufgeräumt" - die nächste Session bekommt dann den im Pool bereitstehenden Prozeß. Der Prozeß bleibt(!) dedicated - bis zu deren Ende bedient er ausschließlich diese Session. Für Speicherverwaltung gilt also das gleiche wie für "normale" Dedicated Server Prozesse (kein Wunder - es sind "normale" Dedicated Server-Prozesse).
The new Database Resident Connection Pool is for pooling and reusing dedicated server processes. When the database session ends the associated dedicated server process does not die - it will be cleaned up and placed back into the pool. If another database session is being initiated it gets its server process from the pool. Important: the server process stays dedicated - that means it will serve only one database session. Regarding the memory management the same rules as for "ordinary" dedicated server processes apply (the processes are "ordinary" dedicated server processes).
Und wozu kann man das brauchen, wenn man kein PHP programmiert?
I'm not programming PHP - so what is that good for?
Naja, mit dem neuen Connection Pool kann man bspw. schnellere SQL*Plus-Verbindungen bekommen - auch wenn der Pool in erster Linie für PHP entwickelt wurde, so kann man ihn doch beliebig nutzen - und der einfachste Weg (mit Default-Einstellungen) geht so:
Hmmm ... the new connection pool allows you to get faster SQL*Plus connections - the fact that the pool was primarily developed for PHP users does not prevent any other developer from using it. The most simple way to use the connection pool is as follows:
begin
  dbms_connection_pool.start_pool;
end;
Dieses Kommando startet den Connection Pool. Auch wenn die Prozeduren im Package vermuten lassen, dass man mehrere Pools in der Datenbank haben kann, so trifft das nicht zu - es kann nur einen (Connection Pool) geben. Mit der Prozedur DBMS_CONNECTION_POOL.CONFIGURE_POOL kann der Pool konfiguriert werden. Wenn man bei der Datenbankverbindung nun den Connection Pool nutzen möchte, muss man das einfach beim TNS-Connection-String angeben (dieses Beispiel nutzt die easy connect syntax):
This command starts the connection pool. The procedures in the package let assume that there can be multiple connection pools with different settings in the database - but this is not true: there can only be one (connection pool). The procedure DBMS_CONNECTION_POOL.CONFIGURE_POOL allows to change the pool configuration. To use the pool when connection to a database instance one has only to supply a keyword in the TNS connection string (this example uses the easy connect syntax).
$ sqlplus scott/tiger@database-host:1521/service-name:POOLED
Probiert es aus: Spätestens beim zweiten Mal geht es schneller - einfach weil die Datenbank keinen neuen Serverprozeß mehr starten muss. Und obwohl der Connection Pool in Java nicht nötig ist (Connection Pools für Java gibt es reichlich), lässt sich der Unterschied mit dem folgenden Java-Programm sehr überzeugend darstellen.
Just give it a try: At least with the second connect you should experience a faster connection - just because the database has not to explicitly start the server process. And although the connection pool is not needed for java (java programmers can choose between a bunch of connection pools), the following java program shows the very difference between using and not using the pool.
import java.sql.*;

public class ConnPoolTest {

  static long lTimeStart;

  public static void main(String args[]) throws Exception {
    DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
    Connection con = null;

    lTimeStart = System.currentTimeMillis();
    System.out.println("*** Start: going to connect 50 times ...");
    System.out.println("*** ... 1st: without \"database resident connection pool\":");
    for (int i=0;i<50;i++) {
      con = DriverManager.getConnection(
        "jdbc:oracle:oci8:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA = (SERVICE_NAME=orcl.doag.org)))","scott","tiger"
      );
      System.out.print(".");
      con.close();
    }
    System.out.println("*** time elapsed: " +(System.currentTimeMillis() - lTimeStart)+"ms.");
    lTimeStart = System.currentTimeMillis();
    System.out.println("*** ... 2nd: WITH \"database resident connection pool\":");
    for (int i=0;i<50;i++) {
      con = DriverManager.getConnection(
        "jdbc:oracle:oci8:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA = (SERVICE_NAME=orcl.doag.org)(SERVER=POOLED)))","scott","tiger"
      );
      System.out.print(".");
      con.close();
    }
    System.out.println("*** time elapsed: " +(System.currentTimeMillis() - lTimeStart)+"ms.");
  }
}
Starten ...
Start it ...
$ java ConnPoolTest
*** Start: going to connect 50 times ...
*** ... 1st: without "database resident connection pool":
..................................................
*** time elapsed: 25410ms.

*** ... 2nd: WITH "database resident connection pool":
..................................................
*** time elapsed: 1477ms.

$
25 Sekunden vs 1.5 Sekunden - das ist schon ein Unterschied! Beachtet dabei aber, dass dazu ein 11g-Client nötig ist (nur der kann das Schlüsselwört POOLED richtig an den Datenbankserver übergeben). Und beim Java-Programm war natürlich der 11g OCI-Treiber vonnöten.
25 vs 1.5 seconds - that's a difference! But note that an 11g Client is required (earlier clients cannot communicate the "pooled connection request" to the database server). And for the java program (of course) the 11g OCI driver was needed.

Beliebte Postings