如何在内存受限系统上下载带有cx_Oracle的巨大Oracle LOB?

时间:2012-10-01 14:02:20

标签: python oracle blob cx-oracle lob

我正在开发一个系统的一部分,其中进程限制在大约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())
当我们点击一​​个大于300-350MB的文件时,lob.read()显然会失败MemoryError,所以我们尝试了类似这样的内容而不是一次性阅读:

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文件来说,这需要两个小时)。为什么2000字节?根据Oracle文档,dbms_lob.substr()将其返回值存储在RAW中,RAW限制为2000字节。

有没有什么方法可以将dbms_lob.substr()结果存储在一个更大的数据对象中,一次读取几兆字节?如何使用cx_Oracle执行此操作?

1 个答案:

答案 0 :(得分:1)

我认为lob.read()中的参数顺序在代码中是相反的。第一个参数应该是偏移量,第二个参数应该是要读取的量。这可以解释O(n)时间和内存使用情况。