如何在内存受限的系统上使用 cx_Oracle 下载巨大的 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())
lob.read()
显然会失败 MemoryError
当我们遇到一个大于 300-350MB 的文件时,我们尝试了这样的方法,而不是一次性读取它:
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 个字节(啊),并且需要很长时间(1.5GB 文件大约需要两个小时)。为什么是2000字节?根据 Oracle 文档, dbms_lob.substr()
将其返回值存储在 RAW 中,该值限制为 2000 字节。
有什么方法可以存储 dbms_lob.substr()
会产生更大的数据对象并且一次可能读取几兆字节?如何使用 cx_Oracle 执行此操作?
解决方案
我认为 lob.read() 中的参数顺序在您的代码中是相反的。第一个参数应该是偏移量,第二个参数应该是要读取的数量。这可以解释 O(n) 时间和内存使用情况。
不隶属于 StackOverflow