2012-10-01 51 views
6

我正在開發一個系統的一部分,進程被限制在大約350MB的RAM;我們使用cx_Oracle從外部系統下載文件進行處理。如何在內存受限系統上使用cx_Oracle下載巨大的Oracle LOB?

外部系統存儲文件爲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做到這一點?

回答

1

我認爲lob.read()中的參數順序在你的代碼中是相反的。第一個參數應該是偏移量,第二個參數應該是要讀取的量。這將解釋O(n)時間和內存使用情況。

+0

哇,我簡直不敢相信那是錯的。 * facepalm *謝謝! –

相關問題