2012-10-01 33 views
6

Estoy desarrollando parte de un sistema donde los procesos están limitados a alrededor de 350MB de RAM; usamos cx_Oracle para descargar archivos de un sistema externo para su procesamiento.¿Cómo descargar enormes Oracle LOB con cx_Oracle en un sistema de memoria limitada?

Los archivos externos sistema almacena como gotas, y podemos agarrar a hacer algo como esto:

# ... set up Oracle connection, then 
cursor.execute(u"""SELECT filename, data, filesize 
        FROM FILEDATA 
        WHERE ID = :id""", id=the_one_you_wanted) 
filename, lob, filesize = cursor.fetchone() 

with open(filename, "w") as the_file: 
    the_file.write(lob.read()) 

lob.read(), obviamente, un error con MemoryError cuando llegamos a un archivo de más de 300-350MB, por lo que hemos intentado algo como esto en lugar de leer todo a la vez:

read_size = 0 
chunk_size = lob.getchunksize() * 100 
while read_size < filesize: 
    data = lob.read(chunk_size, read_size + 1) 
    read_size += len(data) 
    the_file.write(data) 

por desgracia, todavía nos llegan MemoryError después de varias iteraciones. Desde el momento en que se toma lob.read(), y la condición de falta de memoria que finalmente recibimos, parece que lob.read() está extrayendo (tamaño_tamaño + tamaño_datos) bytes de la base de datos cada vez. Es decir, las lecturas toman O (n) tiempo y O (n) memoria, aunque el almacenamiento intermedio es bastante más pequeño.

Para evitar esto, hemos intentado algo así como:

read_size = 0 
while read_size < filesize: 
    q = u'''SELECT dbms_lob.substr(data, 2000, %s) 
      FROM FILEDATA WHERE ID = :id''' % (read_bytes + 1) 
    cursor.execute(q, id=filedataid[0]) 
    row = cursor.fetchone() 
    read_bytes += len(row[0]) 
    the_file.write(row[0]) 

Esto empuja a 2000 bytes (argh) a la vez, y toma siempre (algo así como dos horas para un archivo de 1,5 GB). ¿Por qué 2000 bytes? De acuerdo con los documentos de Oracle, dbms_lob.substr() almacena su valor de retorno en un RAW, que está limitado a 2000 bytes.

¿Hay alguna manera de que pueda almacenar los resultados de dbms_lob.substr() en un objeto de datos más grande y leer unos pocos megabytes a la vez? ¿Cómo hago esto con cx_Oracle?

Respuesta

1

Creo que el orden de los argumentos en lob.read() se invierte en su código. El primer argumento debe ser el desplazamiento, el segundo argumento debe ser la cantidad a leer. Esto explicaría el tiempo de O (n) y el uso de la memoria.

+0

Guau, no puedo creer que eso sea lo que estaba mal. * facepalm * ¡Gracias! –

Cuestiones relacionadas