Comment télécharger un énorme Oracle LOB avec cx_Oracle sur un système à mémoire limitée ?

StackOverflow https://stackoverflow.com//questions/12674806

Question

Je développe une partie d'un système où les processus sont limités à environ 350 Mo de RAM ;nous utilisons cx_Oracle pour télécharger des fichiers à partir d'un système externe pour traitement.

Le système externe stocke les fichiers sous forme de BLOB, et nous pouvons les récupérer en faisant quelque chose comme ceci :

# ... 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() échouera évidemment avec MemoryError lorsque nous atteignons un fichier de plus de 300 à 350 Mo, nous avons donc essayé quelque chose comme ceci au lieu de tout lire d'un coup :

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)

Malheureusement, nous obtenons toujours MemoryError après plusieurs itérations.À partir du moment lob.read() prend, et la condition de manque de mémoire que nous obtenons finalement, il semble que lob.read() extrait ( chunk_size + read_size ) octets de la base de données à chaque fois.Autrement dit, les lectures prennent du temps O(n) et de la mémoire O(n), même si le tampon est un peu plus petit.

Pour contourner ce problème, nous avons essayé quelque chose comme :

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])

Cela extrait 2 000 octets (argh) à la fois et prend une éternité (quelque chose comme deux heures pour un fichier de 1,5 Go).Pourquoi 2000 octets ?Selon la documentation Oracle, dbms_lob.substr() stocke sa valeur de retour dans un format RAW, limité à 2 000 octets.

Existe-t-il un moyen de stocker le dbms_lob.substr() entraîne un objet de données plus volumineux et lit peut-être quelques mégaoctets à la fois ?Comment faire cela avec cx_Oracle ?

Était-ce utile?

La solution

Je pense que l'ordre des arguments dans lob.read() est inversé dans votre code.Le premier argument doit être le décalage, le deuxième argument doit être le montant à lire.Cela expliquerait le temps O(n) et l'utilisation de la mémoire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top