2011-12-08 21 views
6

Me gustaría saber la forma más eficaz de memoria para extraer campos de datos arbitrariamente grandes desde un DB de Oracle con Perl DBI. El método que sé usar es establecer el atributo 'LongReadLen' en el manejador de la base de datos a algo suficientemente grande. Sin embargo, mi aplicación necesita extraer varios miles de registros, por lo que hacer esto arbitrariamente es extremadamente ineficiente en la memoria.Perl DBI alternativa a LongReadLen

El doc sugiere hacer una consulta por adelantado para encontrar el mayor valor posible y establecerlo.

$dbh->{LongReadLen} = $dbh->selectrow_array(qq{ 
    SELECT MAX(OCTET_LENGTH(long_column_name)) 
    FROM table WHERE ... 
}); 
$sth = $dbh->prepare(qq{ 
    SELECT long_column_name, ... FROM table WHERE ... 
}); 

Sin embargo, esto sigue siendo ineficaz, ya que los datos periférica no es representativo de todos los registros. Los valores más grandes superan un MB, pero el registro promedio es inferior a KB. Quiero poder extraer toda la información (es decir, sin truncamiento) mientras desperdicio la menor memoria posible en los almacenamientos intermedios no utilizados.

Un método que he considerado es extraer los datos en fragmentos, digamos 50 registros por vez, y establecer LongReadLen contra la longitud máxima de registros de ese fragmento. Otro trabajo alternativo, que podría, pero no tiene por qué basarse en la idea del fragmento, sería bifurcar un proceso secundario, recuperar los datos y luego matar al niño (llevando consigo la memoria desperdiciada). Lo más maravilloso sería la capacidad de forzar los búferes de DBI, pero no creo que eso sea posible.

¿Alguien ha solucionado un problema similar con algún éxito? ¡Gracias por la ayuda!

EDITAR

Perl v5.8.8, DBI v1.52

Para aclarar: la ineficiencia de memoria está viniendo de uso 'LongReadLen' junto con {ora_pers_lob => 1} en el prepararse. Utilizando este código:

my $sql = "select myclob from my table where id = 68683"; 
my $dbh = DBI->connect("dbi:Oracle:$db", $user, $pass) or croak $DBI::errstr; 

print "before"; 
readline(*STDIN); 

$dbh->{'LongReadLen'} = 2 * 1024 * 1024; 
my $sth = $dbh->prepare($sql, {'ora_pers_lob' => 1}) or croak $dbh->errstr; 
$sth->execute() or croak('Cant execute_query '. $dbh->errstr . ' sql: ' . $sql); 
my $row = $sth->fetchrow_hashref; 

print "after"; 
readline(*STDIN); 

uso de memoria residente "antes" que está en 18 MB y su uso "después" que está en 30 MB. Esto es inaceptable en un gran número de consultas.

+0

¿Pensaste en hacerlo de forma "paralela"? – amrfaissal

+0

¿Qué quiere decir con "extremadamente ineficiente en la memoria"? Creo que cualquier impuesto a la memoria LongReadLen es _per instrucción handle_, no por fila recuperada. (Es decir, buscar mil registros con LOBs no es una pena de memoria mil veces mayor). De hecho, la medición de 'DBD :: Oracle' 1.36 con' Devel :: Size' sugiere que un LOB "buffer de búsqueda" es tan importante como el más grande LOB ha ido tan lejos. Por lo tanto, si mantener un solo búfer grande es molesto, quizás simplemente podría ORDER BY LENGTHB (long_col) y dejar que DBI crezca según sea necesario. – pilcrow

+0

Actualicé la publicación con más detalles.No mencioné mi uso de ora_pers_lob; tienes razón en que LongReadLen, por sí solo, no está hambriento de memoria. Usar LongReadLen sin {ora_pers_lob => 1} en la preparación resultó en ORA-24812, que acabo de resolver. Así que creo que mi pregunta está respondida: ahora puedo usar LongReadLen con el resto del mundo. –

Respuesta

5

¿Están sus columnas con grandes LOB de datos (CLOB o BLOB)? Si es así, no necesita usar LongReadLen en absoluto; DBD :: Oracle proporciona una interfaz de transmisión LOB.

Lo que se quiere hacer es bind the param tipo ORA_CLOB o ORA_BLOB, que te llevará a un "localizador de LOB" regresar de la consulta, en lugar de tex. Luego usa ora_lob_read junto con el localizador de LOB para obtener datos. Aquí hay un ejemplo de código que funcionó para mí:

sub read_lob { 
    my ($dbh, $clob) = @_; 

    my $BLOCK_SIZE = 16384; 

    my $out; 
    my $offset = 1; 

    while (my $data = $dbh->ora_lob_read($clob, $offset, $BLOCK_SIZE)) { 
    $out .= $data; 
    $offset += $BLOCK_SIZE; 
    } 
    return $out; 
} 
+1

+1 para la API especial de LOB. ¿Pero no '' devolverá $ out'' copiará lo que podría ser un escalar muy grande? – pilcrow

+0

Sí, pero al menos solo se usa cuando sea necesario. Y, por supuesto, si es posible procesar los datos de forma continua, puede hacerlo en lugar de '$ out. = $ Data'. – hobbs

+0

Derecha. Si el uso de la memoria es la preocupación, la transmisión al disco o algún otro procesamiento sería un buen enfoque. (También puede devolver '\ $ out' para evitar la copia.) – pilcrow

0

pienso en ello de esta manera:

use Parallel::ForkManager 
use strict; 

# Max 50 processes for parallel data retrieving 
my $pm = new Parallel::ForkManager(50); 

# while loop goes here 
while (my @row = $sth->fetchrow_array) { 

# do the fork 
$pm->start and next; 

# 
# Data retreiving goes here 
# 

# do the exit in the child process 
$pm->finish; 
} 
$pm->wait_all_children; 

cheque Parallel::ForkManager en CPAN saber más.

+0

Gracias por la sugerencia. Si no puedo resolver mi problema ORA-24812, probablemente tomaré esta ruta. –

Cuestiones relacionadas