Moltiplicazione dei cursori con JDBC

lunedì 1 giugno 2009 alle 01:59 | Pubblicato su Performance Tuning, SQL | 3 commenti
Tag: , , ,

Oggi ho fatto un piccolo test con un programma java che inserisce dati in una tabella un po’ di record. Il test nasce da una segnalazione che ho avuto molto tempo fa. Il test mi ha dato occasione di approfondire un po’ l’argomento JDBC e secondo me entra nell’argomento “applicazione indipendenti dal database”. Infatti ho verificato come in certe condizioni, l’utilizzo di JDBC standard, senza le estensioni oracle,  si può avere una proliferazione di cursori nella Shared Pool di Oracle con inevitabili conseguenze sulle prestazioni.

Cominciamo dall’inizio: tempo fa mi giunse la segnalazione che in corrispondenza dell’esecuzione di una particolare procedura (java) si aveva un picco di consumo della CPU da parte di Oracle, al punto da saturare questa risorsa sulla macchina e rallentare tutte le altre attività. Mi venne subito segnalato che vi era una proliferazione di cursori per uno statement. La cosa pareva strana, in quanto l’applicazione fa un uso abbastanza rigororoso di “bind variables”, ma un esame della vista V$SQL_SHARED_CURSOR evidenziava diversi “BIND MISMATCH”. Effettivamente, nonostante l’uso delle bind variables, vi erano delle situzioni che oggi ho finalmente approfondito, la frequente assenza di valori per alcune colonne, mai le stesse per i diversi insert. La tabella in oggetto avveva un numero elevato di campi ed ogni insert aveva dei null fra i valuri da inserire, non sempre nelle stesse colonne.

Illustro meglio cosa intendo dire con un esempio, ho una tabella T così fatta:


SVILUPPO40@perseo10 > desc t
Nome   Nullo?   Tipo
------ -------- ------------
A               NUMBER
B               NUMBER
C               TIMESTAMP(6)

e un programmino java di cui riporto un pezzo:


PreparedStatement stmt = _connection1.prepareStatement("INSERT /* - 1 */ INTO T (A, B, C) VALUES (?,?,?)");
stmt.setLong(1,1);
stmt.setLong(2,11);
stmt.setObject(3,ts);
int retval = stmt.executeUpdate();
stmt.setLong(1,1);
stmt.setLong(2,11);
stmt.setObject(3,null);
retval = stmt.executeUpdate();
stmt.close();

Inserisco due record, nel primo nella terza colonna passo un oggetto ts (è un timestamp java), nel secondo passo null. Ebbene, interrrogando la V$SQL il risultato è :


SQL_ID        CHILD_NUMBER EXECUTIONS SQLTXT
------------- ------------ ---------- ------------------------------
3823umzxhg563            0          1 INSERT /* - 1 */ INTO T (A, B,
3823umzxhg563            1          1 INSERT /* - 1 */ INTO T (A, B,

Utilizzando uno dei comodissimi script messi a disposizione da Dion Cho:


SYSTEM@perseo10 > @shared_cursor
SYSTEM@perseo10 > set echo off
Immettere un valore per 1: INSERT /* - 1 */ INTO T%
vecchio  14:           and q.sql_text like ''&1''',
nuovo  14:           and q.sql_text like ''INSERT /* - 1 */ INTO T%''',
SQL_TEXT                       = INSERT /* - 1 */ INTO T (A, B, C) VALUES (:1,:2,:3)
SQL_ID                         = 3823umzxhg563
ADDRESS                        = 2A54B848
CHILD_ADDRESS                  = 24957B88
CHILD_NUMBER                   = 0
--------------------------------------------------
SQL_TEXT                       = INSERT /* - 1 */ INTO T (A, B, C) VALUES (:1,:2,:3)
SQL_ID                         = 3823umzxhg563
ADDRESS                        = 2A54B848
CHILD_ADDRESS                  = 24977EB8
CHILD_NUMBER                   = 1
BIND_MISMATCH                  = Y
--------------------------------------------------

E infine approfondendo con la vista V$SQ_BIND_METADATA:


SYSTEM@perseo10 > select * from v$sql_bind_metadata where  address='24957B88';

ADDRESS    POSITION   DATATYPE MAX_LENGTH  ARRAY_LEN BIND_NAME
-------- ---------- ---------- ---------- ---------- -------------------------
24957B88          3        180         11          0 3
24957B88          2          2         22          0 2
24957B88          1          2         22          0 1

SYSTEM@perseo10 > select * from v$sql_bind_metadata where  address='24977EB8';

ADDRESS    POSITION   DATATYPE MAX_LENGTH  ARRAY_LEN BIND_NAME
-------- ---------- ---------- ---------- ---------- -------------------------
24977EB8          3          1         32          0 3
24977EB8          2          2         22          0 2
24977EB8          1          2         22          0 1

Mi pare interessante notare che quando da java viene fatto setObject(3,null), Oracle prende il tipo della bind variable 1 che da documentazione corrisponde a varchar2.

Dando un’occhiata alla documentazione Java ho notato l’esistenza di un metodo setNull dell’interfaccia PreparedStatement e l’ho provato:


/* caso tre */ 
 stmt = _connection1.prepareStatement("INSERT /* - 3 */ INTO T (A, B, C) VALUES (?,?,?)");
 stmt.setLong(1,1);
 stmt.setLong(2,11);
 stmt.setObject(3,ts);
 retval = stmt.executeUpdate();
 stmt.setLong(1,1);
 stmt.setNull(2,java.sql.Types.DECIMAL);
 stmt.setObject(3,ts);
 retval = stmt.executeUpdate();
 stmt.close();

in questo caso non c’è bind mismatch, infatti:


SQL_ID        CHILD_NUMBER EXECUTIONS SQLTXT
------------- ------------ ---------- ------------------------------
0zsbqbdnk1ssx            0          2 INSERT /* - 3 */ INTO T (A, B,

il cursore viene riutilizzato (le esecuzioni sono due e non ci sono altri child).

Per gestire tutti i propri tipi dato Oracle ha introdotto delle estensioni nei driver JDBC, ad esempio ha esteso l’interfaccia PreparedStatement con l’interfaccia OraclePreparedStatement, nella documentazione viene proprio spiegato questo e come utilizzarlo con un esempio.

Si vede quindi che anche la programmazione con JDBC richiede particolare attenzione per evitare situazioni di “incompatibilità di tipo dato” (datatype mismatch) che possono in ultimo incidere negativamente sulle prestazioni del database server Oracle

3 commenti »

RSS feed for comments on this post. TrackBack URI

  1. Interessante spunto, ho iniziato anch’io qualche indagine per vedere se c’è qualche type mismatch, oltre a quelli già trovati in passato per via del piano di esecuzione sbagliato🙂

  2. …in compenso ho trovato che le per alcune update e insert i child cursor si sprecano. A quanto pare ciò dipende dalla lunghezza delle variabili di bind esattamente come viene detto qui: http://dioncho.wordpress.com/2009/03/05/vsql_shared_cursor/
    il problema è che però è quasi esclusivamente sulle insert e update (non le select); i programmatori mi dicono che la lunghezza delle stringhe nelle variabili di bind non è nota a priori, e varia anche di molto.
    Nelle viste di sistema la lunghezza delle variabili di bind di tipo varchar2 viene arrotondata al numero più vicino tra 32, 128, 2000 e 4000; in questo modo ogni volta si ha un nuovo child cursor e un bind mismatch.
    Non capisco se tutto ciò influisca sulle prestazioni in qualche modo; per ora non mi pare nel mio caso particolare, ma mi piacerebbe saperne di più.

  3. si certo, questo perchè la lunghezza delle bind variables è uno degli aspetti che concorrono a rendere un cursore condivisibile/shareable o meno

    uno spunto interessante in un post del solito Tom:

    http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9497064796920

    Alessandro


Lascia un commento

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

Crea un sito o un blog gratuitamente presso WordPress.com.
Entries e commenti feeds.

%d blogger cliccano Mi Piace per questo: