2012-04-26 14 views
25

Ho una procedura PL/SQL che esegue molto SUBSTR s su un parametro VARCHAR2. Vorrei rimuovere il limite di lunghezza, quindi ho provato a cambiarlo in CLOB.Prestazioni di SUBSTR su CLOB

Funziona bene, ma le prestazioni ne soffrono, quindi ho eseguito alcuni test (basato su these tests dal 2005).


UPDATE: posso riprodurre questo su diversi casi diversi con diverse versioni di Oracle e hardware differente, dbms_lob.substr è sempre evidente più lento di substr(CLOB), e molto più lento di SUBSTR(VARCHAR2).

I risultati di Bob e i test nel link sopra raccontano una storia diversa.

Qualcuno può spiegare questo, o almeno riprodurre i risultati di Bob o dei miei? Grazie!


I risultati dei test:

+000000000 00:00:00. (VARCHAR2)
+000000000 00:00:00. (CLOB SUBSTR)
+000000000 00:00:00. 356.000.000 (DBMS_LOB.SUBSTR) Codice

prova:

DECLARE 
    l_text VARCHAR2(30) := 'This is a test record'; 
    l_clob CLOB := l_text; 
    l_substr VARCHAR2(30); 
    t TIMESTAMP; 
BEGIN 
    t := SYSTIMESTAMP; 
    FOR i IN 1..100000 LOOP 
    l_substr := SUBSTR(l_text,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (VARCHAR2)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..100000 LOOP 
    l_substr := SUBSTR(l_clob,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (CLOB SUBSTR)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..100000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,14,1); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR)'); 
END; 
+0

Nota: il test tre è '14,1' dove gli altri sono' 1,14'. Proverò anche qualcosa come "10000, 5000" perché il punto è che stai cercando di infrangere il limite di 4k di VARCHAR.Inoltre, poiché i risultati sono circa 75x più lenti per non VARCHAR, sei in grado di esaminare un algoritmo che si occupa di più VARCHAR? * [Come una tabella normalizzata in cui un campo è il 'sequence_id' che mostra la posizione relativa di questa stringa e l'altro è il VARCHAR] *. Infine, sebbene vi sia una grande differenza * relativa *, la differenza * absolute * è bassa. Quindi, importa? * [Pre-ottimizzazione] * – MatBailie

+1

@Dems: Grazie per il tuo contributo! '14,1' e' 1,14' sono corretti (grazie a Oracle per le API coerenti). Sto cercando di interrompere il limite di byte '32767' (PL/SQL, non SQL), e i risultati sono più o meno gli stessi quando si usa il testo con quella lunghezza' (LPAD ('X', 32767, 'X')) '. Ho pensato a questa soluzione a più tavoli varchar, ma mi piacerebbe evitarlo :) Ed è importante, dato che la procedura viene chiamata molto spesso, ma soprattutto sono curioso di sapere se ci sono alternative ... –

+1

Sulle mie macchine DBMS_LOB.SUBSTR è leggermente più lento di CLOB_SUBSTR (20%). Ed entrambi mooore più lentamente di varchar2 (70 volte più lento). Corro su 11gR2. –

risposta

17

(Lies, maledette bugie, e parametri di riferimento ...)

I ri-eseguito il test 10 volte, espandendo la stringa in modo da avere una lunghezza di 30 caratteri e ottenuto i seguenti risultati medi:

+000000000 00:00:00.011694200 (VARCHAR2) 
+000000000 00:00:00.901000600 (CLOB SUBSTR) 
+000000000 00:00:00.013169200 (DBMS_LOB.SUBSTR) 

Ho poi cambiato il campo di stringa a 5,14 (14,5 per DBMS_LOB.SUBSTR) e ottenuto:

+000000000 00:00:00.011731000 (VARCHAR2) 
+000000000 00:00:01.010840000 (CLOB SUBSTR) 
+000000000 00:00:00.011427000 (DBMS_LOB.SUBSTR) 

Poi ho cambiato il campo a 17,14 (14,17 per DBMS_LOB.SUBSTR) e ottenuto

+000000000 00:00:00.013578900 (VARCHAR2) 
+000000000 00:00:00.964527400 (CLOB SUBSTR) 
+000000000 00:00:00.011416800 (DBMS_LOB.SUBSTR) 

Infine, ho cambiato la gamma a 25,14 (14,25 per DBMS_LOB.SUBSTR) e ottenuto

+000000000 00:00:00.011210200 (VARCHAR2) 
+000000000 00:00:00.916439800 (CLOB SUBSTR) 
+000000000 00:00:00.013781300 (DBMS_LOB.SUBSTR) 

la mia conclusione è esimo quando si lavora contro CLOB è meglio usare DBMS_LOB.SUBSTR perché sembra non avere alcuna penalità di prestazioni rispetto all'uso di SUBSTR rispetto a un VARCHAR2 "normale". SUBSTR contro un CLOB sembra soffrire di una significativa penalizzazione delle prestazioni. Per la cronaca - OS = HP/UX (variante Unix), versione Oracle = 11.1, processore = HP Itanium 2-plex. YMMV.

Condividi e divertiti.


E perché se vale la pena fare vale la pena di over-fare, ecco alcuni più risultati con le corde espanso a 32767 caratteri. Substring gamme di date con ogni set di risultati:

1, 25000 
+000000000 00:00:00.198466400 (VARCHAR2) 
+000000000 00:00:02.870958700 (CLOB SUBSTR) 
+000000000 00:00:00.174490100 (DBMS_LOB.SUBSTR) 

1000, 25000 
+000000000 00:00:00.253447900 (VARCHAR2) 
+000000000 00:00:02.491790500 (CLOB SUBSTR) 
+000000000 00:00:00.193560100 (DBMS_LOB.SUBSTR) 

10000, 25000 
+000000000 00:00:00.217812000 (VARCHAR2) 
+000000000 00:00:02.268794800 (CLOB SUBSTR) 
+000000000 00:00:00.222200200 (DBMS_LOB.SUBSTR) 

stesso giorno, stessa conclusione.

Cthulhu fhtagn.


(Un altro assalto alla breccia, cari amici, ancora una volta ...)

Re-corse dei parametri di riferimento, cambiando la dimensione del CLOB a 3.276.700, e prendendo la sottostringa dalla partenza di mezzo a 2475000 per la lunghezza 25000 ottengo:

+000000000 00:00:00.176883200 (VARCHAR2) 
+000000000 00:00:02.069482600 (CLOB SUBSTR) 
+000000000 00:00:00.175341500 (DBMS_LOB.SUBSTR) 

(nota che le modifiche interessano solo le ultime due prove).

AND ... stesso risultato, giorno diverso.

YMMV.

+0

Grazie! Ho provato il tuo ultimo test (estendendo 'l_text' a 50 caratteri, dal momento che 30 non ha più senso), ma i risultati erano ancora paragonabili al mio test originale (dopo averlo chiamato più volte):' 0.006', '0.679',' 1.064'. Quale versione di Oracle stai usando? Puoi riprodurre i risultati quando chiami più volte? –

+0

Oracle 11.1. E sì, i risultati dei test sono ripetibili. Ho cambiato i loop per iterare 1 milione di volte ciascuno, quindi ho diviso l'intervallo di tempo risultante per 10 per renderli approssimativamente paragonabili ai test originali. Ho ripetuto i test più volte e ogni risultato è stato paragonabile. –

+0

È strano. Ho provato i miei test su tre diverse istanze (11.2 su Windows, 11.2 su Linux e 10.2 su Linux), ei risultati sono più o meno gli stessi del mio post. Sarebbe bello avere altri risultati da altre persone ... –

2

Ran lo script per tre volte sul seguente sistema:

Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - produzione 64bit

Ecco i risultati:

+000000000 00:00:00.007787000 (VARCHAR2) 
+000000000 00:00:03.093258000 (CLOB SUBSTR) 
+000000000 00:00:00.340017000 (DBMS_LOB.SUBSTR) 

+000000000 00:00:00.019460000 (VARCHAR2) 
+000000000 00:00:03.302425000 (CLOB SUBSTR) 
+000000000 00:00:00.336915000 (DBMS_LOB.SUBSTR) 

+000000000 00:00:00.007773000 (VARCHAR2) 
+000000000 00:00:03.210619000 (CLOB SUBSTR) 
+000000000 00:00:00.336689000 (DBMS_LOB.SUBSTR) 
+0

Grazie! Ti dispiacerebbe condividere quale sistema operativo utilizzi, nel caso questo faccia la differenza? –

+0

@PeterLang Certo. Redhat Enterprise Linux: 2.6.9-67.ELsmp – tp9

2

vedo quello sui test 11gR1 è andato liscio per DBMS_LOB.substr, ma per 11gR2 la funzione è lenta.

Sotto il mio test su Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production su AIX6.

+000000000 00:00:00.009440000 (VARCHAR2) 
+000000000 00:00:00.749113000 (CLOB SUBSTR) 
+000000000 00:00:01.177685000 (DBMS_LOB.SUBSTR) 
2

So che questo è molto vecchio, ma potrebbe essere ancora rilevante per le persone su sistemi precedenti. Questo sembra un problema di conversione del tipo di dati. Sulla base di qualcosa che ho notato osservando l'effetto @ bernhard.weingartner ha visto, il tipo di dati degli argomenti di offset e quantità sembra fare una grande differenza.

Questo viene eseguito su 11.2.0.3 su Linux (OEL 5.6), e aumentato a un milione di iterazioni solo per fare le differenze ancora più evidente:

DECLARE 
    l_text VARCHAR2(30) := 'This is a test record'; 
    l_clob CLOB := l_text; 
    l_substr VARCHAR2(30); 
    t TIMESTAMP; 
BEGIN 
    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := SUBSTR(l_text,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (VARCHAR2)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := SUBSTR(l_clob,1,14); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (CLOB SUBSTR)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,14,1); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with 14,1)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,14.0,1.0); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with 14.0,1.0)'); 

    t := SYSTIMESTAMP; 
    FOR i IN 1..1000000 LOOP 
    l_substr := DBMS_LOB.SUBSTR(l_clob,cast(14 as number), cast(1 as number)); 
    END LOOP; 
    dbms_output.put_line(SYSTIMESTAMP - t || ' (DBMS_LOB.SUBSTR with casts)'); 
END; 
/
+000000000 00:00:00.043019000 (VARCHAR2) 
+000000000 00:00:03.671621000 (CLOB SUBSTR) 
+000000000 00:00:05.704337000 (DBMS_LOB.SUBSTR with 14,1) 
+000000000 00:00:00.040097000 (DBMS_LOB.SUBSTR with 14.0,1.0) 
+000000000 00:00:00.040907000 (DBMS_LOB.SUBSTR with casts) 

Il mostrano la formale parametri come tipo INTEGER, ma in realtà il passaggio di un intero (o pls_integer o binary_double) è lento, mentre il passaggio esplicito di un numero è veloce.

Dalla domanda originale e dai risultati di Bob questo sembra essere cambiato tra 11.1 e 11.2. Non ho un'istanza 12c da testare, quindi non so se è cambiato di nuovo. Se è dovuto a una modifica in dbms_lob oa una modifica più ampia a come PL/SQL gestisce i valori numerici per impostazione predefinita non è chiaro. Non ho trovato nulla su MOS che appaia rilevante.