2012-10-01 2 views
6

Ich entwickle einen Teil eines Systems, in dem Prozesse auf etwa 350 MB RAM beschränkt sind; Wir verwenden cx_Oracle, um Dateien von einem externen System zur Verarbeitung herunterzuladen.Wie lade ich riesige Oracle LOB mit Cx_Oracle auf Speicher-Constrained-System?

das externe System speichert Dateien als BLOBs, und wir können sie so etwas wie dies greifen tun:

# ... 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() wird natürlich mit MemoryError fehlschlagen, wenn wir eine Datei größer als 300-350MB getroffen, so wir haben so etwas wie dies versucht stattdessen alles auf einmal zu lesen:

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) 

Leider haben wir noch MemoryError nach mehreren Iterationen erhalten. Von der Zeit lob.read() nimmt, und die Out-of-Memory-Bedingung, die wir schließlich bekommen, sieht es aus, als ob lob.read() zieht (chunk_size + read_size) Bytes aus der Datenbank jedes Mal. Das heißt, Lesevorgänge benötigen O (n) Zeit und O (n) Speicher, obwohl der Puffer ziemlich viel kleiner ist.

Um dies zu umgehen, haben wir versucht, so etwas wie:

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

Dies zieht 2000 Byte (argh) zu einem Zeitpunkt, und dauert ewig (so etwas wie zwei Stunden für eine 1,5 GB-Datei). Warum 2000 Bytes? Laut Oracle-Dokumentation speichert dbms_lob.substr() seinen Rückgabewert in einem RAW, der auf 2000 Byte begrenzt ist.

Gibt es eine Möglichkeit, die dbms_lob.substr() Ergebnisse in einem größeren Datenobjekt speichern und vielleicht ein paar Megabytes gleichzeitig lesen? Wie mache ich das mit cx_Oracle?

Antwort

1

Ich denke, dass die Argument Reihenfolge in lob.read() in Ihrem Code umgekehrt ist. Das erste Argument sollte der Offset sein, das zweite Argument sollte der zu lesende Betrag sein. Dies würde die O (n) -Zeit und die Speichernutzung erklären.

+0

Wow, ich kann nicht glauben, dass das was falsch war. * facepalm * Danke! –