메모리가 제한된 시스템에서 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바이트(argh)를 가져오며 시간이 오래 걸립니다(1.5GB 파일의 경우 2시간 정도).왜 2000바이트인가요?오라클 문서에 따르면, dbms_lob.substr()
반환 값을 2000바이트로 제한되는 RAW에 저장합니다.
저장할 수 있는 방법이 있나요? dbms_lob.substr()
더 큰 데이터 개체가 생성되고 한 번에 몇 메가바이트를 읽을 수 있습니까?cx_Oracle로 이 작업을 어떻게 수행합니까?
해결책
코드에서 lob.read()의 인수 순서가 반대인 것 같습니다.첫 번째 인수는 오프셋이어야 하고, 두 번째 인수는 읽을 양이어야 합니다.이것은 O(n) 시간과 메모리 사용량을 설명합니다.