6

プロセスが約350MBのRAMに制限されているシステムの一部を開発しています。cx_Oracleを使用して、処理のために外部システムからファイルをダウンロードします。

外部システムはファイルをBLOBとして保存し、次のようにファイルを取得できます。

# ... 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()MemoryError300〜350 MBを超えるファイルをヒットすると、明らかに失敗するため、一度にすべてを読み取るのではなく、次のようなものを試しました。

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)

残念ながら、私たちはまだMemoryError数回の反復の後で得ます。時間lob.read()がかかり、最終的にメモリ不足状態になると、毎回lob.read()データベースから(chunk_size + read_size)バイトをプルしているように見えます。つまり、バッファがかなり小さい場合でも、読み取りにはO(n)時間とO(n)メモリが必要です。

これを回避するために、次のような方法を試しました。

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

これは一度に2000バイト(argh)をプルし、永久にかかります(1.5GBファイルの場合は2時間程度)。なぜ2000バイトなのか?Oracleのドキュメントによると、dbms_lob.substr()戻り値はRAWに格納されます。RAWは2000バイトに制限されています。

dbms_lob.substr()結果をより大きなデータオブジェクトに保存し、一度に数メガバイトを読み取る方法はありますか?cx_Oracleでこれを行うにはどうすればよいですか?

4

1 に答える 1

1

コードでは、lob.read()の引数の順序が逆になっていると思います。最初の引数はオフセットである必要があり、2番目の引数は読み取る量である必要があります。これは、O(n)時間とメモリ使用量を説明します。

于 2012-10-30T23:37:57.793 に答える