Skip to Main Content
  • Questions
  • Selection of JDBC client side driver - Thin Vs OCI

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Nick.

Asked: November 29, 2000 - 4:48 pm UTC

Last updated: February 03, 2012 - 4:02 pm UTC

Version: 8.1.6

Viewed 10K+ times! This question is

You Asked

Hi Tom,

As always, thanks for your help. I have a regarding which JDBC client side driver to use: Thin or OCI.

Our web application uses Servlets and JSPs (we do not use applets), which are housed in a Tomcat engine. We presently use the thin driver to connect to an Oracle8i (8.1.6) database.

Do you think there would be any benefit of moving to the JDBC OCI drivers.

Also, what would require to change if I do that - For eg. will I have to include an Oracle client and relevant drivers on Tomcat. What other impact can it have wrt to deployment, performance, maintenance etc.

Thanks in advance,

Nic

and Tom said...

When deploying to a client machine -- I prefer to use the THIN drivers.

When deploying on a server machine, I prefer to use the OCI drivers.

The reason is mostly performance. The OCI drivers, in my experience, tend to be faster, especially with some operations like LOBS and such.

It will require the installation of the Oracle8 client on the machine with the tomcat engine. It should not increase maintenance issues at all (instead of upgrading the thin jdbc drivers you would upgrade the Oracle8 client software which has the jdbc drivers in it).

What I would do first however is have both available so I can switch BACK to the thin drivers at the drop of a hat. They are different pieces of code and you may find some behaviour between the two to be different.

Rating

  (22 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

tomcat / Oracle OCI Driver

A reader, September 30, 2002 - 9:50 pm UTC

Hi Tom,

we have the same implemantation
orcle - (oracle client -- tomcat)

but rigth now we are facing the problem of the connection pooling.
ok. we create a connection pool and from that we use the
connection as an when needed. Now,
If the database session is abruptly killed
(
1.) due to havy network traffic
2.) some other reason.
)
the oracle client OCI does not genereate any exception.
so we can not check if the connection is lost/dropped
and disply the appropriate message
and Noclassfound error is displayed on the resulting JSP page.

we are on linux 73 platform running oracle 8.1.7.4
at back-end and 8.1.7.4 client with tomcat4

Do you have any suggestion for this.

secondally,
we have checked that with the Thin JDBC driver,
this issue does not exist.
But, then we have to query the database each time.
our team is using
==> select sysdate from dual
to check if the connection exists

I think this is very expansive.
do you have any suggestion for this.

=> do you think if we use
select '1' from dual
might be cheaper stmt.(But still it's a call)

Thanks,
Vivek





Tom Kyte
October 01, 2002 - 9:53 am UTC

check out devtrends.oracle.com


select sysdate from dual vs select '1' from dual. select whatever you like, you are splitting very extremely thin fine hairs at that point.

oci vs. thin counter argument article

john, December 31, 2002 - 2:11 pm UTC

tom,

please briefly skim this link which gives an excerpt for jdbc thin vs oci for 8.1.7. it almost says, always use thin, it performs better. use statements, instead of preparedstatements because they perform better without discussing the impact on the database, only in terms of a single apps metrics. i wouldn't have given it a second look if it wasn't an o'reilly jdbc book. Is this still accurate for 9i oci drivers? or is this info just extremely short sighted with regards to overall db impact?

</code> http://www.onjava.com/lpt/a//onjava/excerpt/oraclejdbc_19/index.html <code>



Tom Kyte
December 31, 2002 - 2:48 pm UTC

It has never been true as far as I know.

OCI driver has always offered more features.

OCI driver has always been for me more performant -- especially with blob/clob operations.



this paragraph:

...
There's a popular belief that using a PreparedStatement object is faster than using a Statement object. After all, a prepared statement has to verify its metadata against the database only once, while a statement has to do it every time. So how could it be any other way? Well, the truth of the matter is that it takes about 65 iterations of a prepared statement before its total time for execution catches up with a statement. This has performance implications for your application, and exploring these issues is what this section is all about.

....

is totally wrong, the guy was not looking at the database obviously. He was looking at the execution of a single statement in isolation. I hope he never develops on my database -- since his stupid Statement will never use bind variables!

This:

....
When it comes to which SQL statement object performs better under typical use, a Statement or a PreparedStatement, the truth is that the Statement object yields the best performance. When you consider how SQL statements are typically used in an application--1 or 2 here, maybe 10-20 (rarely more) per transaction--you realize that a Statement object will perform them in less time than a PreparedStatement object. In the next two sections, we'll look at this performance issue with respect to both the OCI driver and the Thin driver.
.......

is truly frightening. Downright foolish in fact. That would be the STUPID, foolish way to do it (i have harsher words but, this is a public forum and all).


That article just annoys me -- all rubbish.


Your Attention please Mr. Kyte

Mohammed Khan, January 01, 2003 - 4:52 am UTC

package perftest;
import java.sql.*;
import oracle.jdbc.OracleDriver;
import java.util.Date;

public class PerfTest
{


static void doStatement (Connection con, int count) throws Exception
{
long start = new Date().getTime();
p ("doing statement start time : " + start);
Statement st = con.createStatement();

for (int i = 0; i < count; i++)
{
st.executeUpdate("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)" +
" values (" + i +
", 'ST - code" + i + "'" +
", 'St - descr" + i + "'" +
", user, sysdate ) ");
}
long end = new Date().getTime();
p("end time : " + end);
p ("done statment " + count + " times in " + (end - start) + " milli seconds");
}

static void doPreparedStatement (Connection con, int count) throws Exception
{
long start = new Date().getTime();
p ("doing prepared statement start time : " + start);
PreparedStatement ps = con.prepareStatement("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)"
+ " values (?,?,?, user, sysdate)");

for (int i = 0; i < count; i++)
{
ps.setInt(1,i);
ps.setString(2,"PS - code" + i);
ps.setString(3,"PS - desc" + i);
ps.executeUpdate();
}
long end = new Date().getTime();
p("end time : " + end);
p ("done prepared statment " + count + " times in " + (end - start) + " milli seconds");

}

public static void main (String arr[]) throws Exception
{
/*
* Note : My DB is 9i Rel2, and classes12.jar which comes with this REL.
* It is (classes12.jar) also available in JDEVeloper 903

* I am flushing the shared pool after each run
* and also truncating the table testxxxperf
*
*
*/
//this statement is needed for every run
Connection con = null;
DriverManager.registerDriver(new oracle.jdbc.OracleDriver());

//using thin driver
/* The Statement test with count of 100 gave me :

doing statement start time : 1041410728351
end time : 1041410728945
done statment 100 times in 594 milli seconds

Now with 1000 inserts

doing statement start time : 1041411410918
end time : 1041411413699
done statment 1000 times in 2781 milli seconds


And Finally with 1 Insert
doing statement start time : 1041413168367
end time : 1041413168492
done statment 1 times in 125 milli seconds

These results are similar to what the author (in the article) has shown.

*
*
*/
con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "demo", "demo");
con.setAutoCommit(false);
//doStatement (con, 100);
//doStatement (con, 1000);
//doStatement (con, 1);
/* doing prepared statement
*
doing prepared statement start time : 1041411007525
end time : 1041411008134
done prepared statment 100 times in 609 milli seconds
*
* I dont see much difference between the two with 100 inserts.
*
doing prepared statement start time : 1041411195667
end time : 1041411197307
done prepared statment 1000 times in 1640 milli seconds

With One Insert
doing prepared statement start time : 1041413303289
end time : 1041413303461
done prepared statment 1 times in 172 milli seconds

*
*
*/
//doPreparedStatement(con, 100);
//doPreparedStatement(con, 1000);
doPreparedStatement(con, 1);
con.commit();
con.close();
//con = DriverManager.getConnection ("jdbc:oracle:oci:@ORCL","demo", "demo");
// dont have time to test with OCI Driver

}

static void p (String s)
{
System.out.println (s);
}
}



-----------------------------------------------------------

What does the above implies ?

This program is executed against oracle DB 9i Rel 2.

For a moment keep aside shared pool, hard parse and soft parse and talk about PreparedStatement and Statement as they are the only way to execute statements from java.

If you have more than 1000 inserts for every 2 seconds then use
PreparedStatement or else using statement will perform better.





Tom Kyte
January 01, 2003 - 9:06 am UTC

warning: totally harsh rebuke coming.


what does this imply? It implies you have learned nothing from this site. It implies you haven't read anything about scaling up or testing things to scale. It implies you cannot see the forest for the trees.


You ask me to "keep aside for a moment" exactly that which you must be looking at -- the most important stuff!

Tell me -- while you may not execute the same statement 1,000 times your program -- what are the ODDS that your statement is executed 1,000 times ACROSS ALL PROCESSES???

Tell me -- what happens when two users do this at the same time? Do you know what a latch is? Do you understand the ramifications of flooding the shared pool with literal SQL? Do you???

Time after time after time after time I go into a site -- "database slow" they say. Well heck, it is falling over sideways PARSING STATEMENTS -- "what happened with you tested with 100 users" I ask? "test with more then one user? why bother - it just scales up all by itself right?"

I see you say you didn't have the time to test with the OCI driver (but you did have time to write this -- hmmm -- given using the oci driver would be chaning one line?) Anyway -- you need to TAKE the time to test things properly.


Sure, if I could say "in a dust free lab, on a pure frictionless surface -- do this test (that is your single user mode looking at elapsed times and nothing else, nothing relevant to seeing how this will really work)" I might come to the same mistaken, false, wrong conclusion you did.


I find your test to be flawed as well -- not that I even want to validate its premise but -- a java programmer typically codes:

create statment
execute statment
close statement

not
create statment once
execute over and over


So, moving the statment creation inside your loop as would be more normal -- the break even comes at under 20 for me.....


But that aside, because that isn't what is relevant here tell me what happens when you have N users (n>1) SIMULTANEOUSLY doing a statment.execute of a unqiue statement? Describe to me in detail what happens in the database. then and only then will you see why what you've said is a really truly baaaadddd idea.


This is exactly why I think the best java programs never have the words

o select
o insert
o update
o delete
o merge

in them -- only BEGIN ... END; calls to stored procedures (preferablly with an OUT parameter -- FORCING you to use a CallableStatement even! Making it so that statements don't even work!)



Touched a sort spot you did -- I cannot believe how many times and in how many ways I have to say the same thing over and over and over again. Bind variables -- use them or LOSE.

You know, this bothered me SO much

Tom Kyte, January 02, 2003 - 8:40 am UTC

This bothered me so much -- so so much, that I just had to followup again. I took the above so called "test" and modified it to meet what I believe would be a true test. I made it multi-user, just to show -- just to prove for once and for all that using statements is a bad bad idea.

Here is my modified java code. Pretty much the same but I log the timings to a database table so we can use SQL to analyze the results and parameterized it so we can use it over and over without recompiling each time:


mport java.sql.*;
import oracle.jdbc.OracleDriver;
import java.util.Date;

public class perftest
{

static PreparedStatement saveTimesPs;
static void saveTimes( Connection con,
String which,
long elap ) throws Exception
{
if ( saveTimesPs == null )
saveTimesPs = con.prepareStatement
("insert into timings " +
"( which, elap ) values "+
"( ?, ? )" );

saveTimesPs.setString(1,which);
saveTimesPs.setLong(2,elap);
saveTimesPs.executeUpdate();
}

static void doStatement (Connection con,
int count) throws Exception
{
long start = new Date().getTime();
Statement st = con.createStatement();
for (int i = 0; i < count; i++)
{
st.executeUpdate
("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)" +
" values (" + i +
", 'ST - code" + i + "'" +
", 'St - descr" + i + "'" +
", user, sysdate ) ");
}
st.close();
con.commit();
long end = new Date().getTime();
saveTimes( con, "STMT", end-start );
}

static void doPreparedStatement (Connection con,
int count) throws Exception
{
long start = new Date().getTime();
PreparedStatement ps =
con.prepareStatement("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)"
+ " values (?,?,?, user, sysdate)");

for (int i = 0; i < count; i++)
{
ps.setInt(1,i);
ps.setString(2,"PS - code" + i);
ps.setString(3,"PS - desc" + i);
ps.executeUpdate();
}
con.commit();
long end = new Date().getTime();
saveTimes( con, "PSTMT", end-start );
}

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

con = DriverManager.getConnection
("jdbc:oracle:oci8:@ora920.us.oracle.com",
// ("jdbc:oracle:thin:@aria-dev:1521:ora920",
"scott", "tiger");

con.setAutoCommit(false);

Integer iters = new Integer(arr[0]);

Statement st = con.createStatement();
System.out.println( "waiting for DO_STMT alert" );
st.execute
( "declare l_msg varchar2(4000); " +
"l_status number; begin " +
"dbms_alert.register( 'DO_STMT' ); " +
"dbms_alert.waitone( 'DO_STMT', l_msg, l_status ); " +
"dbms_alert.remove( 'DO_STMT' ); end;" );

doStatement (con, iters.intValue() );

System.out.println( "waiting for DO_PSTMT alert" );
st.execute
( "declare l_msg varchar2(4000); " +
"l_status number; begin " +
"dbms_alert.register( 'DO_PSTMT' ); " +
"dbms_alert.waitone( 'DO_PSTMT', l_msg, l_status ); " +
"dbms_alert.remove( 'DO_PSTMT' ); end;" );
doPreparedStatement(con, iters.intValue() );

System.out.println( "all done..." );

con.commit();
con.close();
}
}


Now, the test driver I used is this csh script. The goal here is have N users doing M iterations concurrently -- you know, like you might have doing in the real world for example. So, I

a) use sqlplus to flush the shared pool. drop and create the tables. but also warm up the shared pools dictionary cache by parsing and executing some inserts against the tables in question.

b) start up $1 copies of the java program. They all block on dbms_alert, waiting to be told to "go".

c) tell them to go

d) wait for them to ask to do the pstmt

e) tell them to do that

f) print out the results

#!/bin/csh -f

set users = $1
set iters = $2

sqlplus scott/tiger <<EOF
alter system flush shared_pool;

drop table timings;
create table timings ( which varchar2(10), elap number );
insert into timings values ( 'x', 0 );
commit;
delete from timings;

drop table testxxxperf;
create table testxxxperf
( id number, code varchar2(25), descr varchar2(25),
insert_user varchar2(30), insert_date date );
insert into testxxxperf values(0,'x','x','x',sysdate);
commit;
delete from testxxxperf;

exit
EOF


while ( $users > 0 )
java perftest $iters &
@ users = $users - 1
end

echo when all are ready, hit enter...
read

sqlplus scott/tiger <<EOF
exec dbms_alert.signal( 'DO_STMT', '' );
commit;
exit
EOF


echo when all are ready for do_pstmt, hit enter...
read


sqlplus scott/tiger <<EOF
exec dbms_alert.signal( 'DO_PSTMT', '' );
commit;
exit
EOF

echo when all are done, hit enter
read

sqlplus scott/tiger <<EOF
select which, count(*), avg(elap), min(elap), max(elap) from timings group by which;
exit
EOF


amazingly, when I run

$ perftest 10 1


WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
PSTMT 10 41.1 22 75
STMT 10 71.7 26 141



10 users doing a single statement. or how about 10 users doing 10 statements:

$ perftest 10 10

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
PSTMT 10 83.1 37 155
STMT 10 144.7 94 272

or even 1 user doing 1 statement:

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
PSTMT 1 26 26 26
STMT 1 28 28 28


now, that was OCI thick, lets try thin:

$ perfstat 10 1

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
PSTMT 10 49 27 81
STMT 10 102.7 39 154


(and so on...)

That prepared statement is looking pretty good now..

Tom Kyte
January 02, 2003 - 12:13 pm UTC

And you know what -- I was wondering why MY test ran the PSTMT in the same time as the STMT -- yet, yours did not.

I run your test:

done statment 1 times in 26 milli seconds
done prepared statment 1 times in 69 milli seconds

and it is very very very consistent. Now, I run mine (without the dbms_alert waits -- just let it run) and I get:

STMT (14)
PSTMT (14)


very very consistently. So, questions:

a) why does my STMT run faster than yours? we run the same code
b) why does my PSTMT run faster than yours? we run the same code.

Hmmm -- well, I have some statment.executes BEFORE I call yours....
My modified statement routine calls Prepared statement stuff BEFORE I call doPrepare

Hence -- here is what is happening

my code warms up the statement class, gets it all loaded. Your doStatement benefitted from that.

my code then warmed up the prepared statement class, gets it all loaded. Your doPrepared benefitted from that.


After the classes are loaded up -- they are the *same*. Well, no let me rephrase that. They have the same elapsed time in a dust free lab, running single user mode.

The prepared statement is superior in every way....

Here is the modified benchmark that throws out the initial overhead of calling statement/preparedstatement (which in the grand scheme of things is just NOISE). Tell me which is "better" now?


Here is the code (note the offset I added was to make all of the rows inserted into the table UNIQUE -- because we used i in the statement -- i+offset will keep them unique upto 1000 in my case):

import java.sql.*;
import oracle.jdbc.OracleDriver;
import java.util.Date;

public class perftest
{
static void doStatement (Connection con,
int count,
int offset ) throws Exception
{
long start = new Date().getTime();
Statement st = con.createStatement();
for (int i = offset; i < count+offset; i++)
{
st.executeUpdate
("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)" +
" values (" + i +
", 'ST - code" + i + "'" +
", 'St - descr" + i + "'" +
", user, sysdate ) ");
}
st.close();
con.commit();
long end = new Date().getTime();
System.out.println( "STMT" + " (" + (end-start) + ") times="+count );
}

static void doPreparedStatement (Connection con,
int count,
int offset ) throws Exception
{
long start = new Date().getTime();

PreparedStatement ps = con.prepareStatement("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)"
+ " values (?,?,?, user, sysdate)");

for (int i = offset; i < count+offset; i++)
{
ps.setInt(1,i);
ps.setString(2,"PS - code" + i);
ps.setString(3,"PS - desc" + i);
ps.executeUpdate();
}
con.commit();
long end = new Date().getTime();
System.out.println( "PSTMT" + " (" + (end-start) + ") times="+count );
}

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

con = DriverManager.getConnection
//("jdbc:oracle:oci8:@ora920.us.oracle.com",
("jdbc:oracle:thin:@aria-dev:1521:ora920",
"scott", "tiger");

con.setAutoCommit(false);

Integer iters = new Integer(arr[0]);

System.out.println( "IGNORE THESE TIMES" );
doStatement (con, iters.intValue(), 0 );
doPreparedStatement(con, iters.intValue(), 0 );
System.out.println( "IGNORE THESE TIMES" );


System.out.println( "PAY ATTENTION TO THESE TIMES" );
doStatement (con, iters.intValue(), 1000 );
doPreparedStatement(con, iters.intValue(), 1000 );
System.out.println( "PAY ATTENTION TO THESE TIMES" );

con.commit();
con.close();
}
}


here is the script:


#!/bin/csh -f

sqlplus scott/tiger <<EOF
alter system flush shared_pool;

drop table timings;
create table timings ( which varchar2(10), elap number );
insert into timings values ( 'x', 0 );
commit;
delete from timings;

drop table testxxxperf;
create table testxxxperf
( id number, code varchar2(25), descr varchar2(25),
insert_user varchar2(30), insert_date date );
insert into testxxxperf values(0,'x','x','x',sysdate);
commit;
delete from testxxxperf;

exit
EOF

java perftest $1


and here is the output:


$ perftest 1
IGNORE THESE TIMES
STMT (13) times=1
PSTMT (100) times=1
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (8) times=1
PSTMT (8) times=1
PAY ATTENTION TO THESE TIMES


hmmm, stmt = pstmt for a single execution, once the classes are loaded (and how many times do you load a class?)

$ perftest 10
IGNORE THESE TIMES
STMT (37) times=10
PSTMT (108) times=10
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (33) times=10
PSTMT (25) times=10
PAY ATTENTION TO THESE TIMES


Hmmm, 10 statements PSTMT faster

$ perftest 100

IGNORE THESE TIMES
STMT (338) times=100
PSTMT (278) times=100
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (328) times=100
PSTMT (179) times=100
PAY ATTENTION TO THESE TIMES

much faster...

$ perftest 1000

IGNORE THESE TIMES
STMT (3078) times=1000
PSTMT (1724) times=1000
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (3031) times=1000
PSTMT (1794) times=1000
PAY ATTENTION TO THESE TIMES

truly fast

But remember -- its not just about elapsed time, its all about latches!!!



If you don't use prepared statements -- you lose.




Splendid

Jer Smith, January 02, 2003 - 2:10 pm UTC

It's great when you provide simple tests to debunk misinformation.

I know you've got these scattered all over the place, in presentations and whatnot, but for the basic ones:
The implicit cursor myth
The prepared statement myth
The hit-ration myth
The separate index and table myth.

It would be good to consolidate the "debunking" into a single
site.

Also a manageability difference

Jake Spencer, January 02, 2003 - 4:08 pm UTC

It's probably also worth mentioning that because the thin driver doesn't use Net8, you can't do any of the things you would normally be able to do by setting a sqlnet.ora parameter - Oracle Names, sqlnet tracing, etc.

For example, we use Oracle Names in our environment to make database locations transparent to our clients, but now we have clients who are using the thin driver. This means they can't use the Names server to connect, which in turn makes manageability more difficult.

Mohammed Khan, January 04, 2003 - 6:32 am UTC

Help Mr. Kyte !!!!!!!!!

Sorry to bother you so much.

I've modified the unix script to work with windows.

:: r.bat
@ECHO OFF
:: %1 ACCEPTS USERS 
:: %2 ACCEPTS ITERATIONS

sqlplus scott/tiger @G:\perftest\droptabs


FOR /L %%U IN (%1,-1,1) do START JAVA perftest %2


echo when all are ready for do_pstmt, hit enter...
pause

:: running prepared statements first shows drastic difference your and mine results
sqlplus scott/tiger @G:\perftest\signal_pstmt


echo when all are ready, hit enter...
pause

sqlplus scott/tiger @G:\perftest\signal_stmt



echo when all are done, hit enter
pause

sqlplus scott/tiger @G:\perftest\rep



----------------- end of file r.bat

File droptabs.sql

alter system flush shared_pool;

    drop table timings;
    create table timings ( which varchar2(10), elap number );
    insert into timings values ( 'x', 0 );
    commit;
    delete from timings;

    drop table testxxxperf;
    create table testxxxperf
    ( id number, code varchar2(25), descr varchar2(25),
      insert_user varchar2(30), insert_date date );
    insert into testxxxperf values(0,'x','x','x',sysdate);
    commit;
    delete from testxxxperf;

    exit

------------------------ end droptabs.sql

File signal_stmt.sql

    exec dbms_alert.signal( 'DO_STMT', '' );
    commit;
    exit

------------------------ end signal_stmt.sql


File signal_pstmt.sql

    exec dbms_alert.signal( 'DO_PSTMT', '' );
    commit;
    exit

------------------------ end signal_pstmt.sql


File rep.sql

select which, count(*), avg(elap), min(elap), max(elap) from timings group
by which
/

------------------------ end rep.sql


Despite severel runs I am getting the following results.
I've just modified the perftest.java to first register 'DO_PSTMT' alert
and the tables are seemed to be turned, or my machine is insane.

Here is the source of modified perftest.java

import java.sql.*;
import oracle.jdbc.OracleDriver;
import java.util.Date;

public class perftest
{

  static PreparedStatement saveTimesPs;
  static void saveTimes( Connection con,
                         String which,
                         long elap ) throws Exception
  {
    if ( saveTimesPs == null )
        saveTimesPs = con.prepareStatement
                      ("insert into timings " +
                       "( which, elap ) values "+
                       "( ?, ? )" );

    saveTimesPs.setString(1,which);
    saveTimesPs.setLong(2,elap);
    saveTimesPs.executeUpdate();
  }

  static void doStatement (Connection con,
                           int count) throws Exception
  {
    long start = new Date().getTime();
    Statement st = con.createStatement();
    for (int i = 0; i < count; i++)
    {
      st.executeUpdate
      ("insert into testxxxperf " +
       "(id, code, descr, insert_user, insert_date)" +
       " values (" + i  +
       ", 'ST - code" + i + "'" +
       ", 'St - descr" + i + "'" +
       ", user, sysdate ) ");
    }
    st.close();
    con.commit();
    long end = new Date().getTime();
    saveTimes( con, "STMT", end-start );
  }

  static void doPreparedStatement (Connection con,
                                int count) throws Exception
  {
    long start = new Date().getTime();
    PreparedStatement ps = 
       con.prepareStatement("insert into testxxxperf " +
             "(id, code, descr, insert_user, insert_date)"
           + " values (?,?,?, user, sysdate)");

    for (int i = 0; i < count; i++)
    {
      ps.setInt(1,i);
      ps.setString(2,"PS - code" + i);
      ps.setString(3,"PS - desc" + i);
      ps.executeUpdate();
    }
    con.commit();
    long end = new Date().getTime();
    saveTimes( con, "PSTMT", end-start );
  }

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

    con = DriverManager.getConnection
          //("jdbc:oracle:oci:@ORCL", ---- again dont have time to test with OCI, I wonder how         

//you manage your time, work, asktom........
           ("jdbc:oracle:thin:@localhost:1521:ORCL",
          "scott", "tiger");

    con.setAutoCommit(false);

    Integer iters = new Integer(arr[0]);


    Statement st = con.createStatement();

    System.out.println( "waiting for DO_PSTMT alert" );
    st.execute
    ( "declare l_msg varchar2(4000); " +
    "l_status number; begin " +
    "dbms_alert.register( 'DO_PSTMT' ); " +
    "dbms_alert.waitone( 'DO_PSTMT', l_msg, l_status ); " +
    "dbms_alert.remove( 'DO_PSTMT' ); end;" );
    doPreparedStatement(con, iters.intValue() );

    System.out.println( "waiting for DO_STMT alert" );
    st.execute
    ( "declare l_msg varchar2(4000); " +
    "l_status number; begin " +
    "dbms_alert.register( 'DO_STMT' ); " +
    "dbms_alert.waitone( 'DO_STMT', l_msg, l_status ); " +
    "dbms_alert.remove( 'DO_STMT' ); end;" );

    doStatement (con, iters.intValue() );


    System.out.println( "all done..." );

    con.commit();
    con.close();
  }
}

Here goes the results

G:\perftest>r 10 1
when all are ready for do_pstmt, hit enter...
Press any key to continue . . .
when all are ready, hit enter...
Press any key to continue . . .
when all are done, hit enter
Press any key to continue . . .

SQL*Plus: Release 8.0.6.0.0 - Production on Sat Jan 4 13:59:35 2003

(c) Copyright 1999 Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production


WHICH       COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- --------- --------- --------- ---------
PSTMT             10       997       578      1313
STMT              10      65.7        62        79

SQL> exit

-------with 10 * 10 combination

G:\perftest>r 10 10
when all are ready for do_pstmt, hit enter...
Press any key to continue . . .
when all are ready, hit enter...
Press any key to continue . . .
when all are done, hit enter
Press any key to continue . . .

SQL*Plus: Release 8.0.6.0.0 - Production on Sat Jan 4 14:06:44 2003

(c) Copyright 1999 Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production


WHICH       COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- --------- --------- --------- ---------
PSTMT             10    1140.7       860      1547
STMT              10     118.5        31       203

SQL> exit


---------- now with 60 inserts

G:\perftest>r 10 60
when all are ready for do_pstmt, hit enter...
Press any key to continue . . .
when all are ready, hit enter...
Press any key to continue . . .
when all are done, hit enter
Press any key to continue . . .

SQL*Plus: Release 8.0.6.0.0 - Production on Sat Jan 4 14:09:44 2003

(c) Copyright 1999 Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production


WHICH       COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- --------- --------- --------- ---------
PSTMT             10    1890.7      1172      2656
STMT              10     642.3       234      1156

SQL> exit

-------- finally with 10 * 1000 inserts

G:\perftest>r 10 1000
when all are ready for do_pstmt, hit enter...
Press any key to continue . . .
when all are ready, hit enter...
Press any key to continue . . .
when all are done, hit enter
Press any key to continue . . .

SQL*Plus: Release 8.0.6.0.0 - Production on Sat Jan 4 14:12:31 2003

(c) Copyright 1999 Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.1.0 - Production


WHICH       COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- --------- --------- --------- ---------
PSTMT             10    9201.5      3422     13547
STMT              10   10952.8      5485     15093

SQL> exit

--------------------------------------------------------------------------------


Mr. Kyte I am a big fan of using bind variables and PreparedStatements, but after seeing
the article and doing the tests, I am forced to think the other way.

I own a book named "Expert One On One Oracle"
"Chapter 3 Locking and concurrency" The author goes into the issues about locking and concurency 

in great details, and I understood that chapter.




The following is the single user test environment, but it doe'nt matter as one should scale with 

multiple users not with single user.



Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-2000 Microsoft Corp.

G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 1
IGNORE THESE TIMES
STMT (16) times=1
PSTMT (109) times=1
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (0) times=1
PSTMT (16) times=1
PAY ATTENTION TO THESE TIMES


G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 1
IGNORE THESE TIMES
STMT (0) times=1
PSTMT (109) times=1
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (0) times=1
PSTMT (16) times=1
PAY ATTENTION TO THESE TIMES

After executing two times also same results.

G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 2
IGNORE THESE TIMES
STMT (0) times=2
PSTMT (110) times=2
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (15) times=2
PSTMT (16) times=2
PAY ATTENTION TO THESE TIMES


G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 3
IGNORE THESE TIMES
STMT (0) times=3
PSTMT (125) times=3
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (0) times=3
PSTMT (0) times=3
PAY ATTENTION TO THESE TIMES

after 10 iters also

G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 10
IGNORE THESE TIMES
STMT (31) times=10
PSTMT (125) times=10
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (31) times=10
PSTMT (32) times=10
PAY ATTENTION TO THESE TIMES


G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 1000
IGNORE THESE TIMES
STMT (2234) times=1000
PSTMT (1172) times=1000
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (2172) times=1000
PSTMT (1000) times=1000
PAY ATTENTION TO THESE TIMES

G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>java perftest.perftest 60
IGNORE THESE TIMES
STMT (172) times=60
PSTMT (188) times=60
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (140) times=60 ---------note this
PSTMT (641) times=60
PAY ATTENTION TO THESE TIMES

G:\JDEV903\jdev\mywork\JDBCPerfTest\TomTest\classes>
 

Tom Kyte
January 04, 2003 - 9:35 am UTC


you are MISSING THE ENTIRE POINT. You you missing the point -- I just totally give up. (it is ALL about what happens with more then one user, what happens with one user here - as i keep trying to say - it abosolutely meaningless.)

Do what you want, go ahead, -- but you know what -- stay away -- far far far away from my dataases PLEASE. Your databases are the ones that have latch free waits like crazy, ora-4031's, people sitting there waiting to flush the shared pool, enormous performance spikes. Go for it -- if that is what you want. Just go for it. Remember, its all about that 0.01 second you are trying to save in the java client, seems worth it doesn't it?

(don't you see something wrong with your testing -- look at that last one -- perhaps you need to look inside your database --


GNORE THESE TIMES
STMT (172) times=60
PSTMT (188) times=60 <<<<==========
IGNORE THESE TIMES
PAY ATTENTION TO THESE TIMES
STMT (140) times=60 ---------note this
PSTMT (641) times=60 <<<<=========
PAY ATTENTION TO THESE TIMES

or your JVM. turn off garbage collection for example -- did you stop to ask "hmm, why 188 and then 641??? wonder, just wonder what is going on there? )


And you are so wrong, so so wrong with your statement that a single user test is even valid. You are so missing the point -- so missing the point.


funny thing is -- how do you explain these:

PAY ATTENTION TO THESE TIMES
STMT (15) times=2
PSTMT (16) times=2
PAY ATTENTION TO THESE TIMES

PAY ATTENTION TO THESE TIMES
STMT (0) times=3
PSTMT (0) times=3
PAY ATTENTION TO THESE TIMES

Hmm, apparently -- just apparently -- on your machine, you should run 3 statements in all programs cause that is faster then 2???

Do you think there might -- just might -- be some bursty performance issues here on your machine? That you might -- just might -- have to run this test like 50 times and average the numbers out?


Ok, let me do that for you. here is a modified yet again perftest.java:


import java.sql.*;
import oracle.jdbc.OracleDriver;
import java.util.Date;

public class perftest
{
static PreparedStatement saveTimesPs;
static void saveTimes( Connection con,
String which,
long elap ) throws Exception
{
System.out.println( which + " (" + elap + ")" );
if ( saveTimesPs == null )
saveTimesPs = con.prepareStatement
("insert into timings " +
"( which, elap ) values "+
"( ?, ? )" );

saveTimesPs.setString(1,which);
saveTimesPs.setLong(2,elap);
saveTimesPs.executeUpdate();
}

static void doStatement (Connection con,
int count,
int offset,
String which ) throws Exception
{
long start = new Date().getTime();
Statement st = con.createStatement();
for (int i = offset; i < count+offset; i++)
{
st.executeUpdate
("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)" +
" values (" + i +
", 'ST - code" + i + "'" +
", 'St - descr" + i + "'" +
", user, sysdate ) ");
}
st.close();
con.commit();
long end = new Date().getTime();
System.out.println( "STMT" + " (" + (end-start) + ") times="+count );
saveTimes( con, which, (end-start) );
}

static void doPreparedStatement (Connection con,
int count,
int offset,
String which ) throws Exception
{
long start = new Date().getTime();

PreparedStatement ps = con.prepareStatement("insert into testxxxperf " +
"(id, code, descr, insert_user, insert_date)"
+ " values (?,?,?, user, sysdate)");

for (int i = offset; i < count+offset; i++)
{
ps.setInt(1,i);
ps.setString(2,"PS - code" + i);
ps.setString(3,"PS - desc" + i);
ps.executeUpdate();
}
con.commit();
long end = new Date().getTime();
System.out.println( "PSTMT" + " (" + (end-start) + ") times="+count );
saveTimes( con, which, (end-start) );
}

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

con = DriverManager.getConnection
//("jdbc:oracle:oci8:@ora920.us.oracle.com",
("jdbc:oracle:thin:@aria-dev:1521:ora920",
"scott", "tiger");

con.setAutoCommit(false);

Integer iters = new Integer(arr[0]);

System.out.println( "IGNORE THESE TIMES" );
doStatement (con, 1, 0, "I-STMT" );
doPreparedStatement(con, 1, 0, "I-PSTMT" );
System.out.println( "IGNORE THESE TIMES" );


System.out.println( "PAY ATTENTION TO THESE TIMES" );
doStatement (con, iters.intValue(), 1000, "STMT" );
doPreparedStatement(con, iters.intValue(), 1000, "PSTMT" );
System.out.println( "PAY ATTENTION TO THESE TIMES" );

con.commit();
con.close();
}
}

and a perftest script:

#!/bin/csh -f

set ntimes = $1
set iters = $2

sqlplus scott/tiger<<EOF
drop table timings;
create table timings ( which varchar2(10), elap number );
insert into timings values ( 'x', 0 );
commit;
delete from timings;
exit
EOF


while( $ntimes > 0 )

sqlplus scott/tiger <<EOF
alter system flush shared_pool;

drop table testxxxperf;
create table testxxxperf
( id number, code varchar2(25), descr varchar2(25),
insert_user varchar2(30), insert_date date );
insert into testxxxperf values(0,'x','x','x',sysdate);
commit;
delete from testxxxperf;

exit
EOF

java perftest $iters

@ ntimes = $ntimes - 1

end


sqlplus scott/tiger<<EOF
select which, count(*), avg(elap), min(elap), max(elap)
from timings
group by which;
exit
EOF


$ perftest 10 1 <<<<=== run 10, single insert tests one after the other


WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 13.2 9 17
I-STMT 10 15.6 11 20
PSTMT 10 8.3 6 15
STMT 10 13.6 13 15


and the thing about testing is consistency -- I want to make sure that if I run this again and again -- the results are infinitely reproducible -- and they are:


WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 11.1 8 16
I-STMT 10 16.9 12 22
PSTMT 10 7.4 6 15
STMT 10 14.3 13 22


WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 15.2 9 25
I-STMT 10 17.3 11 28
PSTMT 10 6.7 6 7
STMT 10 16.2 13 23


WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 21.4 9 93
I-STMT 10 17.6 12 26
PSTMT 10 8.8 6 16
STMT 10 16 13 29


it is very very consistent -- how about yours? that was 9ir2 vs 9ir2, here is 817 vs 817:

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 13.1 12 16
I-STMT 10 17.1 13 27
PSTMT 10 12.4 6 16
STMT 10 13.1 13 14

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 16.6 10 26
I-STMT 10 17.7 12 31
PSTMT 10 10.7 6 22
STMT 10 13.3 13 15

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 17.1 12 30
I-STMT 10 18.2 13 41
PSTMT 10 14.9 10 24
STMT 10 13.1 12 14

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 10 13 10 25
I-STMT 10 15.1 12 18
PSTMT 10 8.5 6 14
STMT 10 15.1 12 25


I seem to be noticing some rather wide variations.... but the numbers are rather close (benefitting pstmts mostly tho).


but the bottom line is again NOT ABOUT a single user.

I guess if you want to build single user systems -- go for it. If you want to build mulit-user systems that actually scale and work -- you better forget about the existence of Statements in jdbc....

Run your test 100 times and average it out over the long haul. See what happens there. If you see something quirky (like the two examples above) explore it -- explain it.

Just for grins, I did 100 iterations of a single statement against 817:

WHICH COUNT(*) AVG(ELAP) MIN(ELAP) MAX(ELAP)
---------- ---------- ---------- ---------- ----------
I-PSTMT 100 16.23 9 45
I-STMT 100 19.69 11 206
PSTMT 100 12.41 6 23
STMT 100 13.82 12 50




and remember my advice from
</code> http://asktom.oracle.com/~tkyte/runstats.html <code>

...
o How many resources each approach takes. This can be more meaningful then even the wall clock timings. For example, if one approach is faster then the other but it takes thousands of latches (locks), I might avoid it simply because it will not scale as well.
....

single user tests -- elapsed times are virtually meaningless.





A couple of things to add...

Peter Tran, April 11, 2004 - 12:43 pm UTC

Hi Tom,

I love your response above. I recently gave a presentation to the development group on why you should use PreparedStatement instead of Statement. I borrowed a lot of information for my presentation from what I learned here and from your books.

Anyway, I had a lot of great responses and questions. I've also learned a few interesting things that I would like to share with our readers here.

First, I modified your program to execute the test using 4 different possible scenarios for inserting data.

1) Using Statement as you have coded above.
2) Using Statement using addBatch() and executeBatch().
3) Using PreparedStatement as you have coded above.
4) Using PreparedStatement using addBatch() and executeBatch().

For example,

private static void doPreparedStatementBatch (
Connection dbConnection,
int count)
throws Exception
{
long start = System.currentTimeMillis();

PreparedStatement ps =
dbConnection.prepareStatement(
"insert into testxxxperf" +
" (id, code, descr, insert_user, insert_date)" +
" values (?, ? , ?, 'PREPAREDSTATEMENT BATCH', sysdate)");

for (int i = 0; i < count; i++)
{
ps.setInt(1, i);
ps.setString(2,"PS - code" + i);
ps.setString(3,"PS - desc" + i);
ps.addBatch();
}
ps.executeBatch();
ps.close();
dbConnection.commit();

long end = System.currentTimeMillis();

saveTimes( dbConnection, count, "PSTATEMENT_BATCH", end-start );
}

Before I get to the results, I would like to add that the scenario 3 is typically how you would code a query using a PreparedStatement with changing predicate clause. Scenario 4 is the only way a batch insert or batch update should be coded with a PreparedStatement.

Here are my results on a 9.2.0.5 database running on RH2.1AS.

Rows Stmt Stmt_Batch PStmt PStmt_Batch
---- ---- ---------- ----- -----------
1 4 2 6 3
2 4 10 6 3
3 5 9 3 3
4 5 5 4 2
5 6 6 5 3
6 7 7 4 3
7 8 7 7 3
8 8 8 6 6
9 10 16 6 3
10 13 10 6 5

Both PreparedStatement scenarios beat out the Statement scenarios.

Rows Stmt Stmt_Batch PStmt PStmt_Batch
---- ---- ---------- ----- -----------
10 17 15 12 5
20 28 18 13 4
30 27 31 18 7
40 36 44 20 7
50 56 54 28 17
60 56 58 49 7
70 64 63 29 9
80 69 72 46 15
90 76 84 40 8
100 90 97 55 6

With larger # of inserts, the batch PreparedStatement is the only way to go. Just in case anyone doesn't get the point yet.

Rows Stmt Stmt_Batch PStmt PStmt_Batch
---- ---- ---------- ----- -----------
500 466 459 272 70
600 553 529 205 40
700 613 604 214 32
800 695 686 233 36
900 766 776 254 39
1000 849 859 283 33

I have a coule of questions. I ran a test with cursor_sharing=force to see what timings I would get.

cursor_sharing=force

Rows Stmt Stmt_Batch PStmt PStmt_Batch
---- ---- ---------- ----- -----------
500 279 274 272 60
600 300 287 196 27
700 340 325 214 41
800 392 385 223 35
900 418 413 252 34
1000 454 475 271 33

So we have evidence that cursor_sharing=force does help, but as you say it's just a "crutch". It only gets you part way there. The only way to achieve the best performance is to fix the code the use PreparedStatement correctly.

I enabled SQL_TRACE with cursor_sharing to see what Oracle was doing underneath.

insert into testxxxperf (id, time, code, descr, insert_user, insert_date) values
(:1, :2, :3 , :4, 'PREPAREDSTATEMENT', sysdate)

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 10 0.00 0.00 0 0 0 0
Execute 10 0.01 0.01 0 23 129 550
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 20 0.01 0.01 0 23 129 550

insert into testxxxperf (id, time, code, descr, insert_user, insert_date) values
(:"SYS_B_0", :"SYS_B_1", :"SYS_B_2", :"SYS_B_3", :"SYS_B_4", sysdate )

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 550 0.04 0.04 0 0 0 0
Execute 550 0.01 0.05 0 552 1675 550
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 1100 0.05 0.09 0 552 1675 550

The 2nd set of numbers are from Oracle applying the cursor_sharing=force, because the bind variables are all "SYS_B_X". Now my questions are:

1) If cursor_sharing=force causes Oracle to replace the literal values with bind variables, why does Oracle still count each SQL statement separately? Here I called the doStatement method 10x. I would figure my "Parse" and "Execute" counts to be the same as the PreparedStatement version.

2) Why are the LIOs so much higher with cursor_sharing? Shouldn't it be closer or the same as the PreparedStatement?

Thanks,
-Peter







Tom Kyte
April 11, 2004 - 1:09 pm UTC

1) because you PARSED 550 times -- a parse is a parse is a parse is a parse.

As outlined in Expert one on one Oracle in chapter 10 on performance -- cursor sharing is a crutch that fixed only ONE sympton (some of the latching due to a hard parse). It is STILL a parse -- a harder than soft parse (extra work to auto-bind and all) -- but a parse is expensive, period.

The only way to make a parse really fast -- not to do it at all.

2) you did 550 singleton things versus saving up lots of work to be done at one. It is the difference between row at a time processing and bulk processing here. You made us get and unget blocks many more times a row at a time.


No if and or buts, you want to do it right and fast? bulk it up, use prepared statements are parse as little as you can.

Just Great

A reader, March 07, 2005 - 3:21 pm UTC


What about Thin in 10g?

Jelena, December 05, 2005 - 5:36 pm UTC

Hi Tom,
great explanations - thanks.
We had thin driver with 9i and had perfomance problems during high load on system, then we switched to oci - and everything was fine.

Now we found that in 10g thin is so much improved that oracle recommends using it. Is it true?

We switched one of applications and so far we didnt see any drawbacks. But we also do not have so much load yet...

What do you recommend for a average OLTP system, is it still 'old rule' use oci as it's native or is thin so much improved now?

Thanks!

Tom Kyte
December 06, 2005 - 5:30 am UTC

the theory is thick and thin are relatively equivalent these days - but you would want to benchmark that with your application.



Regarding JDBC driver and Oracle Dates Issue

Hemal, February 15, 2007 - 7:02 am UTC

Hello Tom,
I am having the following query related to the Oracle Dates and the corresponding JDBC driver.
For one of our projects we were using the JDBC driver - classes12.zip.
Following date was present in the JDBC TIMESTAMP datatype variable 20060331 (Date format is YYYYMMDD).
We were able to successfully store it in the Oracle 8i Database Table having DATE datatype Column.

We are now using the JDBC driver - ojdbc14.jar
Now, following date is present in the JDBC DATE datatype variable 20060331 (Date format is YYYYMMDD).
We were NOT ABLE to successfully store it in the Oracle 10g Database Table having DATE datatype Column.
We are getting an error.

Can you please advice on this issue.
Thanks and Best Regards
-Hemal Deshmukh.
Tom Kyte
February 15, 2007 - 11:58 am UTC

my car won't start
it did yesterday using gas v1.0
it won't today using gas v1.1
it is giving an error.

can you advise?

A reader, February 15, 2007 - 3:01 pm UTC

>>it won't today using gas v1.1 it is giving an error.can you advise?

LOL.. We know Tom drives Hybrid :-)
Tom Kyte
February 16, 2007 - 1:28 pm UTC

hey, you are giving away too many details. someone might be able to deduce what is wrong if you keep doing that.

Help is on the way

Stew Ashton, February 16, 2007 - 4:56 am UTC

Tom, I have notified one of our development teams of your problem. They say the last time they used that version they got a flat tire, so they are in the process of re-inventing the wheel. We have to wait for a reply from the Patent Office before getting final approval, so please be patient.

Please be advised that internal and outsourcing budgets are separate, so I am afraid you are not allowed to use your gas money to take a taxi or use public transportation in the meantime.
Tom Kyte
February 17, 2007 - 9:03 am UTC

Laughing very much OUT LOUD :)

that was great - especially the last bit. I can relate to that - in a prior job I could hire as many programmers as I wanted but only 5 licenses for the compiler and 2 machines to program on.

people - infinite budget
capital expenditures - nope

so, you can have people but not any place to sit them down, no keyboard, etc...

A reader, August 03, 2007 - 5:20 am UTC

I strongly agree, that the OCI JDBC Driver performs far far better then Thin JDBC especially in case of LOBS.

We have been running a Java Application using JDBC OCI Drivers with Oracle 9i.

Now we are planing to upgrade to oracle 10g. I wonder if there are any issues that we may encounter in the JDBC Drivers perspective while upgrading.

Tom, Please guide me through this.

Thank you.
Tom Kyte
August 05, 2007 - 1:32 pm UTC

I strongly disagree - in 2007

In 2007, they are about the same performance wise.

Back to OCI vs. Thin Driver Performance

PFay, August 05, 2008 - 12:25 pm UTC

Tom,
Performance for thin vs. OCI driver for JDBC seems to be a moving target - earlier years you said OCI was better performance, now I hear both: this thread says "roughly equivalent", but another thread ( http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1001391300346444059 ) says OCI has "an edge in performance over the thin drivers". OK, so maybe it's not a great difference. But then I read from Oracle, that "If you want maximum portability and performance, then use the JDBC Thin driver." ( http://download.oracle.com/docs/cd/B28359_01/java.111/b31224.pdf page 1-4). What does one conclude from this? Is there really any important performance difference? LOBs only?



Tom Kyte
August 05, 2008 - 1:14 pm UTC

they are roughly equivalent these days.

the OCI drivers give you full access to all features.

the thin drivers support most, but not everything, the database has to offer (eg: client side result caching in 11g - not supported in the thin drivers)


pick whichever satisfies you feature wise.

waste of time

waste, November 23, 2009 - 5:32 am UTC

waste of time
Tom Kyte
November 23, 2009 - 4:19 pm UTC

yes, you are. thanks for playing.

OCI Vs JDBC Thin Client

b c, October 11, 2011 - 8:54 am UTC

Tom,

We have a 4 node 11gr1 RAC database supporting a web facing application. The web app uses Websphere & The JDBC Thin Client.
When any one of our nodes experiences high CPU consumption, It appears as if Websphere routes most of the connections to this node causing the CPU to max out. The other 3 nodes receive connections as well, but strangely enough they are not evenly distributed. Not appropriately load balanced.
Our TNS is configured in the following format and on any given day load balancing seems to work fine.
db1.world =
(description =
(address = (protocol = tcp)(host = unix7-vip.mydomain)(port = 1526))
(address = (protocol = tcp)(host = unix8-vip.mydomain)(port = 1526))
(address = (protocol = tcp)(host = unix4-vip.mydomain)(port = 1526))
(load_balance = yes)
(failover = on)
(connect_data =
(server = dedicated)
(service_name = db1.world)
(failover_mode =
(type = select)
(method = basic)
)
)
)

db14 =
(description =
(address = (protocol = tcp)(host = unix4-vip.mydomain)(port = 1526))
(connect_data =
(server = dedicated)
(service_name = db1.world)
(instance_name = db14)
)
)

db13 =
(description =
(address = (protocol = tcp)(host = asegp583-vip.mydomain)(port = 1526))
(connect_data =
(server = dedicated)
(service_name = db1.world)
(instance_name = db13)
)
)

db12 =
(description =
(address = (protocol = tcp)(host = unix8-vip.mydomain)(port = 1526))
(connect_data =
(server = dedicated)
(service_name = db1.world)
(instance_name = db12)
)
)

db11 =
(description =
(address = (protocol = tcp)(host = unix7-vip.mydomain)(port = 1526))
(connect_data =
(server = dedicated)
(service_name = db1.world)
(instance_name = db11)
)
)

We were wondering if the JDBC thin client does not support Failover? The Web Team insists that it does and we ( the DB team ) need some concrete evidence that the OCI thick client works better than the JDBC thin client for Failover purposes.
Your input on this is highly appreciated.

Thanks

BC
Tom Kyte
October 11, 2011 - 10:01 am UTC

what does inbalanced connections have to do with failover?

You might consider setting cpu_count to the number of cpus minus one or two and enabling the default resource plan. That will implement instance caging and will prevent more than that number of cpus from being used by sessions at the same time. That will prevent the node evictions you will almost certainly see if one of the nodes is overwhelmed.

OCI Vs JDBC Thin Client

b c, October 11, 2011 - 10:27 am UTC

Thank you for the quick response, We will implement instance caging right away.

What we are wondering is why the imbalanced connections, is it because the thin client does not have logic to determine if the node is overwhelmed and direct the connection to the not overwhelmed nodes. Would OCI help in this case ? Does it have built in logic that determines node load and route the traffic accordingly.

Thanks

BC
Tom Kyte
October 11, 2011 - 11:28 am UTC

It is likely for two reasons:

a) because your connection pool establishes a ton of connections all at once - they might tend to get routed to one node, since it won't report any load.

b) because your connection pool is not RAC aware, it just sees "a database" and tends to use and reuse the first set of connections it established, compounding (a)

What would likely be most helpful would be a connection pool that is RAC aware - such as our connection pooling software.

OCI Vs JDBC Thin Client

b c, October 11, 2011 - 1:27 pm UTC

Tom,

Thanks Again, you are simply the best ..... :)


Oracle's connection pooling software

Stew Ashton, October 11, 2011 - 1:46 pm UTC


I would guess most Java code in the world uses a connection pool managed by a J2EE Server, such as Websphere or Weblogic. It appears that Weblogic has recently integrated the software to which you refer:

"Active GridLink for Oracle RAC: In Oracle WebLogic Server 10.3.4, ... the Universal Connection Pool Java library has been integrated with WebLogic Server". http://download.oracle.com/docs/cd/E17904_01/web.1111/e13737/oracle_rac.htm And in Websphere it is "supported but not certified": http://www-01.ibm.com/support/docview.wss?uid=swg21498877
Tom Kyte
October 11, 2011 - 2:00 pm UTC

thanks!

OCI vs Thin

A reader, October 12, 2011 - 1:37 am UTC

Hi Tom,

Thin drivers don't support session multiplexing, which OCI does. Would that not be a reason for better performance in case of OCI?
Tom Kyte
October 12, 2011 - 7:06 am UTC

why would that help? You have a connection pool bit of software that is not RAC aware and tends to preconnect a lot of sessions (which will likely get bound to a single instance).

I don't believe this to be a thick/thin issue, but a connection pool issue.

Session multiplexing would concentrate a large number of inbound connections into a smaller one - that is true, but your work would still take place on.......... your one node.


Using connection manager with session multiplexing is a good defense against wildly overconfigured connection pools - but in this case - you want to spread the use of the connections over many nodes in RAC - that is something the connection pooling software would have to help with.

OCI and timestamp

Mark, February 03, 2012 - 3:43 pm UTC

Tom,

I'm installing a software product that runs on a Tomcat application server, connected to a database. Normally I would use an OCI driver, but the product's documentation advises against using OCI, claiming: "Oracle’s documentation suggests that their OCI (Type 2) drivers do not properly support Timestamp data types."

Any truth to this statement? I can't seem to find any mention of this in the Oracle docs or on Google.

Thank you.
Tom Kyte
February 03, 2012 - 4:02 pm UTC

Not as far as I know, they should have provided a link or reference. To say something like that isn't very, well, I don't know what to call it.