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?
Guau, no puedo creer que eso sea lo que estaba mal. * facepalm * ¡Gracias! –